diff --git a/pkg/c3/motes.h b/pkg/c3/motes.h index a9fd4b701e..0000d5c593 100644 --- a/pkg/c3/motes.h +++ b/pkg/c3/motes.h @@ -663,6 +663,7 @@ # define c3__ktts c3_s4('k','t','t','s') # define c3__ktwt c3_s4('k','t','w','t') # define c3__ktzp c3_s4('k','t','z','p') +# define c3__l c3_s1('l') # define c3__lamb c3_s4('l','a','m','b') # define c3__lame c3_s4('l','a','m','e') # define c3__lang c3_s4('l','a','n','g') @@ -686,6 +687,7 @@ # define c3__lg c3_s2('l','g') # define c3__lib c3_s3('l','i','b') # define c3__libd c3_s4('l','i','b','d') +# define c3__lick c3_s4('l','i','c','k') # define c3__life c3_s4('l','i','f','e') # define c3__lift c3_s4('l','i','f','t') # define c3__like c3_s4('l','i','k','e') @@ -1082,6 +1084,7 @@ # define c3__smts c3_s4('s','m','t','s') # define c3__snap c3_s4('s','n','a','p') # define c3__so c3_s2('s','o') +# define c3__soak c3_s4('s','o','a','k') # define c3__sock c3_s4('s','o','c','k') # define c3__soft c3_s4('s','o','f','t') # define c3__sole c3_s4('s','o','l','e') diff --git a/pkg/vere/auto.c b/pkg/vere/auto.c index c5b4a57d93..2435458ad4 100644 --- a/pkg/vere/auto.c +++ b/pkg/vere/auto.c @@ -438,6 +438,7 @@ u3_auto_init(u3_pier* pir_u) car_u = _auto_link(u3_unix_io_init(pir_u), pir_u, car_u); car_u = _auto_link(u3_term_io_init(pir_u), pir_u, car_u); car_u = _auto_link(u3_fore_io_init(pir_u), pir_u, car_u); + car_u = _auto_link(u3_lick_io_init(pir_u), pir_u, car_u); return car_u; } diff --git a/pkg/vere/io/lick.c b/pkg/vere/io/lick.c new file mode 100644 index 0000000000..e420057246 --- /dev/null +++ b/pkg/vere/io/lick.c @@ -0,0 +1,646 @@ +/// @file + +#include "vere.h" +#include +#include +#include "noun.h" + +/* u3_chan: incoming ipc port connection. +*/ +typedef struct _u3_chan { + struct _u3_moor mor_u; // message handler + c3_l coq_l; // connection number + c3_o liv_o; // connection live + struct _u3_shan* san_u; // server backpointer +} u3_chan; + +/* u3_shan: ipc port server. +*/ +typedef struct _u3_shan { + uv_pipe_t pyp_u; // server stream handler + c3_l nex_l; // next connection number + struct _u3_port* gen_u; // port backpointer + struct _u3_chan* can_u; // connection list +} u3_shan; + + +/* u3_port: description of an IPC port +*/ +typedef struct _u3_port { + c3_c* nam_c; // name of port + c3_o con_o; + struct _u3_shan* san_u; // server reference + struct _u3_lick* lic_u; // device backpointer + struct _u3_port* nex_u; // next pointer +} u3_port; + +/* u3_lick: a list of devices +*/ +typedef struct _u3_lick { + u3_auto car_u; // driver + c3_c* fod_c; // IPC folder location + u3_cue_xeno* sil_u; // cue handle + struct _u3_port* gen_u; // port list +} u3_lick; + +static const c3_c URB_DEV_PATH[] = "/.urb/dev"; + +/* _lick_string_to_knot(): convert c unix path component to $knot +*/ +static u3_atom +_lick_string_to_knot(c3_c* pax_c) +{ + u3_assert(pax_c); + u3_assert(!strchr(pax_c, '/')); + if ( '!' == *pax_c ) { + pax_c++; + } + return u3i_string(pax_c); +} + +/* _lick_string_to_path(): convert c string to u3_noun $path +** +** c string must begin with the pier path plus mountpoint +*/ +static u3_noun +_lick_string_to_path(c3_c* pax_c) +{ + u3_noun not; + + //u3_assert(pax_c[-1] == '/'); + c3_c* end_c = strchr(pax_c, '/'); + if ( !end_c ) { + return u3nc(_lick_string_to_knot(pax_c), u3_nul); + } + else { + *end_c = 0; + not = _lick_string_to_knot(pax_c); + *end_c = '/'; + return u3nc(not, _lick_string_to_path(end_c + 1)); + } +} + +/* _lick_it_path(): path for ipc files +*/ +static c3_c* +_lick_it_path(u3_noun pax) +{ + c3_w len_w = 0; + c3_c *pas_c; + + // measure + // + { + 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); + pas_c[len_w] = '\0'; + { + u3_noun wiz = pax; + c3_c* waq_c = pas_c; + + while ( u3_nul != wiz ) { + c3_w tis_w = u3r_met(3, u3h(wiz)); + + if ( (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; +} + +/* _lick_send_noun(): jam and send noun over chan. +*/ +static void +_lick_send_noun(u3_chan* can_u, u3_noun nun) +{ + c3_y* byt_y; + c3_d len_d; + + u3s_jam_xeno(nun, &len_d, &byt_y); + u3z(nun); + u3_newt_send((u3_mojo*)&can_u->mor_u, len_d, byt_y); +} + +/* _lick_mote_free(): u3_moat-shaped close callback. +*/ +static void +_lick_moat_free(void* ptr_v, ssize_t err_i, const c3_c* err_c) +{ + c3_free((u3_chan*)ptr_v); +} + +/* _lick_close_cb(): socket close callback. +*/ +static void +_lick_close_cb(uv_handle_t* had_u) +{ + c3_free((u3_shan*)had_u); +} + +/* _lick_moor_poke(): called on message read from u3_moor. +*/ +static void +_lick_moor_poke(void* ptr_v, c3_d len_d, c3_y* byt_y) +{ + u3_weak put; + u3_noun dev, nam, dat, wir, cad; + + u3_chan* can_u = (u3_chan*)ptr_v; + u3_port* gen_u = can_u->san_u->gen_u; + u3_lick* lic_u = gen_u->lic_u; + c3_i err_i = 0; + c3_c* err_c; + c3_c* tag_c; + c3_c* rid_c; + + put = u3s_cue_xeno_with(lic_u->sil_u, len_d, byt_y); + if ( u3_none == put ) { + can_u->mor_u.bal_f(can_u, -1, "cue-none"); + return; + } + if ( c3n == u3r_cell(put, &nam, &dat) ) { + can_u->mor_u.bal_f(can_u, -2, "put-bad"); + u3z(put); + return; + } + + wir = u3nc(c3__lick, u3_nul); + dev = _lick_string_to_path(gen_u->nam_c+1); + cad = u3nt(c3__soak, dev, put); + u3_auto_peer( + u3_auto_plan(&lic_u->car_u, u3_ovum_init(0, c3__l, wir, cad)), + 0, 0, 0); +} + +/* _lick_close_chan(): close given channel, freeing. +*/ +static void +_lick_close_chan(u3_chan* can_u) +{ + u3_shan* san_u = can_u->san_u; + u3_lick* lic_u = san_u->gen_u->lic_u; + u3_port* gen_u = san_u->gen_u; + gen_u->con_o = c3n; + u3_chan* inn_u; + // remove chan from server's connection list. + // + if ( san_u->can_u == can_u ) { + san_u->can_u = (u3_chan*)can_u->mor_u.nex_u; + } + else { + for ( inn_u = san_u->can_u; inn_u; inn_u = (u3_chan*)inn_u->mor_u.nex_u ) { + if ( (u3_chan*)inn_u->mor_u.nex_u == can_u ) { + inn_u->mor_u.nex_u = can_u->mor_u.nex_u; + break; + } + } + } + can_u->mor_u.nex_u = NULL; + + if ( NULL == san_u->can_u ) { + // send a close event to arvo and stop reading. + // + u3_noun wir, cad, dev, dat, mar; + + wir = u3nc(c3__lick, u3_nul); + dev = _lick_string_to_path(gen_u->nam_c+1); + mar = u3i_string("disconnect"); + dat = u3_nul; + + cad = u3nq(c3__soak, dev, mar, dat); + + u3_auto_peer( + u3_auto_plan(&lic_u->car_u, + u3_ovum_init(0, c3__l, wir, cad)), + 0, 0, 0); + } + + u3_newt_moat_stop((u3_moat*)&can_u->mor_u, _lick_moat_free); +} + +/* _lick_moor_bail(): error callback for u3_moor. +*/ +static void +_lick_moor_bail(void* ptr_v, ssize_t err_i, const c3_c* err_c) +{ + u3_chan* can_u = (u3_chan*)ptr_v; + + if ( err_i != UV_EOF ) { + u3l_log("lick: moor bail %zd %s", err_i, err_c); + if ( _(can_u->liv_o) ) { + _lick_send_noun(can_u, u3nq(0, c3__bail, u3i_word(err_i), + u3i_string(err_c))); + can_u->liv_o = c3n; + } + } + _lick_close_chan(can_u); +} + +/* _lick_sock_cb(): socket connection callback. +*/ +static void +_lick_sock_cb(uv_stream_t* sem_u, c3_i tas_i) +{ + u3_shan* san_u = (u3_shan*)sem_u; + u3_port* gen_u = san_u->gen_u; + u3_chan* can_u; + c3_i err_i; + + can_u = c3_calloc(sizeof(u3_chan)); + can_u->mor_u.ptr_v = can_u; + can_u->mor_u.pok_f = _lick_moor_poke; + can_u->mor_u.bal_f = _lick_moor_bail; + can_u->coq_l = san_u->nex_l++; + can_u->san_u = san_u; + err_i = uv_timer_init(u3L, &can_u->mor_u.tim_u); + u3_assert(!err_i); + err_i = uv_pipe_init(u3L, &can_u->mor_u.pyp_u, 0); + u3_assert(!err_i); + err_i = uv_accept(sem_u, (uv_stream_t*)&can_u->mor_u.pyp_u); + u3_assert(!err_i); + u3_newt_read((u3_moat*)&can_u->mor_u); + can_u->mor_u.nex_u = (u3_moor*)san_u->can_u; + san_u->can_u = can_u; + gen_u->con_o = c3y; + + // only send on first connection + if ( NULL == can_u->mor_u.nex_u ) { + u3_noun dev, dat, wir, cad, mar; + wir = u3nc(c3__lick, u3_nul); + dev = _lick_string_to_path(gen_u->nam_c+1); + mar = u3i_string("connect"); + dat = u3_nul; + + cad = u3nq(c3__soak, dev, mar, dat); + u3_auto_peer( + u3_auto_plan(&gen_u->lic_u->car_u, u3_ovum_init(0, c3__l, wir, cad)), + 0, 0, 0); + } +} + +/* _lick_close_sock(): close an port's socket +*/ +static void +_lick_close_sock(u3_shan* san_u) +{ + u3_lick* lic_u = san_u->gen_u->lic_u; + c3_w len_w = strlen(lic_u->fod_c) + strlen(san_u->gen_u->nam_c) + 2; + c3_c* paf_c = c3_malloc(len_w); + c3_i wit_i; + wit_i = snprintf(paf_c, len_w, "%s/%s", lic_u->fod_c, san_u->gen_u->nam_c); + + u3_assert(wit_i > 0); + u3_assert(len_w == (c3_w)wit_i + 1); + + if ( 0 != unlink(paf_c) ) { + if ( ENOENT != errno ) { + u3l_log("lick: failed to unlink socket: %s", uv_strerror(errno)); + } + } + + uv_close((uv_handle_t*)&san_u->pyp_u, _lick_close_cb); + c3_free(paf_c); +} + +/* _lick_mkdirp(): recursive mkdir of dirname of pax_c. +*/ +static c3_o +_lick_mkdirp(c3_c* por_c) +{ + c3_c pax_c[2048]; + + strncpy(pax_c, por_c, sizeof(pax_c)); + + c3_c* fas_c = strchr(pax_c + 1, '/'); + + while ( fas_c ) { + *fas_c = 0; + if ( 0 != mkdir(pax_c, 0777) && EEXIST != errno ) { + return c3n; + } + *fas_c++ = '/'; + fas_c = strchr(fas_c, '/'); + } + return c3y; +} + +/* _lick_init_sock(): initialize socket device. +*/ +static void +_lick_init_sock(u3_shan* san_u) +{ + // the full socket path is limited to about 108 characters, + // and we want it to be relative to the pier. save our current + // path, chdir to the pier, open the socket at the desired + // path, then chdir back. hopefully there aren't any threads. + // + c3_c pax_c[2048]; + c3_i err_i; + c3_c por_c[2048] = "."; + u3_port* gen_u = san_u->gen_u; + + if ( NULL == getcwd(pax_c, sizeof(pax_c)) ) { + u3l_log("lick: getcwd: %s", uv_strerror(errno)); + u3_king_bail(); + } + if ( 0 != chdir(u3_Host.dir_c) ) { + u3l_log("lick: chdir: %s", uv_strerror(errno)); + u3_king_bail(); + } + + strcat(por_c, URB_DEV_PATH); + strcat(por_c, gen_u->nam_c); + + if ( c3y != _lick_mkdirp(por_c) ) { + u3l_log("lick: mkdir %s: %s", por_c, strerror(errno)); + goto _lick_sock_err_chdir; + } + + if ( 0 != unlink(por_c) && errno != ENOENT ) { + u3l_log("lick: unlink: %s", uv_strerror(errno)); + goto _lick_sock_err_chdir; + } + if ( 0 != (err_i = uv_pipe_init(u3L, &san_u->pyp_u, 0)) ) { + u3l_log("lick: uv_pipe_init: %s", uv_strerror(err_i)); + goto _lick_sock_err_chdir; + } + + if ( 0 != (err_i = uv_pipe_bind(&san_u->pyp_u, por_c)) ) { + u3l_log("lick: uv_pipe_bind: %s", uv_strerror(err_i)); + u3l_log("lick: uv_pipe_bind: %s", por_c); + goto _lick_sock_err_chdir; + } + if ( 0 != (err_i = uv_listen((uv_stream_t*)&san_u->pyp_u, 0, + _lick_sock_cb)) ) { + u3l_log("lick: uv_listen: %s", uv_strerror(err_i)); + goto _lick_sock_err_unlink; + } + if ( 0 != chdir(pax_c) ) { + u3l_log("lick: chdir: %s", uv_strerror(errno)); + goto _lick_sock_err_close; + } + return; + +_lick_sock_err_close: + uv_close((uv_handle_t*)&san_u->pyp_u, _lick_close_cb); +_lick_sock_err_unlink: + if ( 0 != unlink(gen_u->nam_c) ) { + u3l_log("lick: unlink: %s", uv_strerror(errno)); + } +_lick_sock_err_chdir: + if ( 0 != chdir(pax_c) ) { + u3l_log("lick: chdir: %s", uv_strerror(errno)); + } + u3_king_bail(); +} + +/* u3_lick_ef_shut(): Close an IPC port +*/ +static void +_lick_ef_shut(u3_lick* lic_u, u3_noun nam) +{ + c3_c* nam_c = _lick_it_path(nam); + + u3_port* cur_u = lic_u->gen_u; + u3_port* las_u = NULL; + + while ( NULL != cur_u ) { + if ( 0 == strcmp(cur_u->nam_c, nam_c) ) { + _lick_close_sock(cur_u->san_u); + if( las_u == NULL ) { + lic_u->gen_u = cur_u->nex_u; + } + else { + las_u->nex_u = cur_u->nex_u; + } + c3_free(cur_u); + return; + } + las_u = cur_u; + cur_u = cur_u->nex_u; + } + // XX We should delete empty folders in the pier/.urb/dev path +} + + +/* u3_lick_ef_spin(): Open an IPC port +*/ +static void +_lick_ef_spin(u3_lick* lic_u, u3_noun nam) +{ + c3_c* nam_c = _lick_it_path(nam); + u3_port* las_u = lic_u->gen_u; + + while ( NULL != las_u ) { + if( 0 == strcmp(las_u->nam_c, nam_c) ) { + c3_free(nam_c); + return; + } + las_u = las_u->nex_u; + } + u3_port* gen_u = c3_calloc(sizeof(*gen_u)); + gen_u->san_u = c3_calloc(sizeof(*gen_u->san_u)); + + gen_u->lic_u = lic_u; + gen_u->san_u->gen_u = gen_u; + gen_u->nam_c = nam_c; + gen_u->con_o = c3n; + + _lick_init_sock(gen_u->san_u); + if ( NULL == las_u) { + lic_u->gen_u = gen_u; + } + else las_u->nex_u = gen_u; +} + +/* _lick_ef_spit(): spit effects to port +*/ +static void +_lick_ef_spit(u3_lick* lic_u, u3_noun nam, u3_noun dat) +{ + c3_c* nam_c = _lick_it_path(nam); + u3_port* gen_u = NULL; + u3_port* cur_u = lic_u->gen_u; + while (cur_u != NULL){ + if( 0 == strcmp(cur_u->nam_c, nam_c) ) { + gen_u = cur_u; + break; + } + cur_u = cur_u->nex_u; + } + if ( NULL == gen_u ) { + u3l_log("lick: spit: gen %s not found", nam_c); + u3z(dat); + return; + } + + if( c3y == gen_u->con_o ) { + _lick_send_noun(gen_u->san_u->can_u, dat); + } + else { + u3_noun dev, wir, cad, mar; + u3z(dat); + wir = u3nc(c3__lick, u3_nul); + dev = _lick_string_to_path(gen_u->nam_c+1); + mar = u3i_string("error"); + dat = u3i_string("not connected"); + cad = u3nq(c3__soak, dev, mar, dat); + u3_auto_peer( + u3_auto_plan(&gen_u->lic_u->car_u, u3_ovum_init(0, c3__l, wir, cad)), + 0, 0, 0); + } +} + + +/* _lick_io_kick(): apply effects. +*/ +static c3_o +_lick_io_kick(u3_auto* car_u, u3_noun wir, u3_noun cad) +{ + u3_lick* lic_u = (u3_lick*)car_u; + + u3_noun tag, i_wir, par; + u3_noun nam, dat, tmp; + c3_o ret_o, dev_o; + + if ( (c3n == u3r_cell(wir, &i_wir, 0)) + || (c3n == u3r_cell(cad, &tag, &tmp)) + || (c3__lick != i_wir) ) + { + ret_o = c3n; + } + else { + if ( (c3__spin == tag) ) { + _lick_ef_spin(lic_u, u3k(tmp)); // execute spin command + ret_o = c3y; + } + else if ( (c3__shut == tag) ) { + _lick_ef_shut(lic_u, u3k(tmp)); // execute shut command + ret_o = c3y; + } + else if ( c3__spit == tag ) { + if ( c3y == u3r_cell(tmp, &nam, &dat) ) { + _lick_ef_spit(lic_u, u3k(nam), u3k(dat)); + } + ret_o = c3y; + } + else { + ret_o = c3n; + } + } + + u3z(wir); + u3z(cad); + return ret_o; +} + +/* _lick_born_news(): initialization complete on %born. +*/ +static void +_lick_born_news(u3_ovum* egg_u, u3_ovum_news new_e) +{ + u3_auto* car_u = egg_u->car_u; + + if ( u3_ovum_done == new_e ) { + car_u->liv_o = c3y; + } +} + +/* _lick_born_bail(): %born is essential, retry failures. +*/ +static void +_lick_born_bail(u3_ovum* egg_u, u3_noun lud) +{ + u3l_log("lick: %%born failure;"); + u3z(lud); + u3_ovum_free(egg_u); +} + +/* _lick_io_talk(): notify %lick that we're live + * pass it a list of device names +*/ +static void +_lick_io_talk(u3_auto* car_u) +{ + u3_lick* lic_u = (u3_lick*)car_u; + u3_noun wir = u3nc(c3__lick, u3_nul); + u3_noun cad = u3nc(c3__born, u3_nul); + + u3_auto_peer( + u3_auto_plan(car_u, u3_ovum_init(0, c3__l, wir, cad)), + 0, + _lick_born_news, + _lick_born_bail); + + car_u->liv_o = c3y; +} + +/* _lick_io_exit(): close all open device files and exit +*/ +static void +_lick_io_exit(u3_auto* car_u) +{ + u3_lick* lic_u = (u3_lick*)car_u; + u3_port* cur_u = lic_u->gen_u; + u3_port* nex_u; + while ( NULL != cur_u ) { + _lick_close_sock(cur_u->san_u); + nex_u = cur_u->nex_u; + c3_free(cur_u); + cur_u = nex_u; + } + + u3s_cue_xeno_done(lic_u->sil_u); + c3_free(lic_u); +} + + +/* u3_lick(): initialize hardware control vane. +*/ +u3_auto* +u3_lick_io_init(u3_pier* pir_u) +{ + u3_lick* lic_u = c3_calloc(sizeof(*lic_u)); + + c3_c pax_c[2048] = ""; + struct stat st = {0}; + strcat(pax_c, u3_Host.dir_c); + strcat(pax_c, URB_DEV_PATH); + + if ( -1 == stat(pax_c, &st) ) { + u3l_log("lick init mkdir %s",pax_c ); + if ( mkdir(pax_c, 0700) && (errno != EEXIST) ) { + u3l_log("lick cannot make directory"); + u3_king_bail(); + } + } + + lic_u->sil_u = u3s_cue_xeno_init(); + lic_u->fod_c = strdup(pax_c); + + u3_auto* car_u = &lic_u->car_u; + car_u->nam_m = c3__lick; + car_u->liv_o = c3n; + car_u->io.talk_f = _lick_io_talk; + car_u->io.kick_f = _lick_io_kick; + car_u->io.exit_f = _lick_io_exit; + + return car_u; +} diff --git a/pkg/vere/pier.c b/pkg/vere/pier.c index 263af55e1a..1e39d14ef4 100644 --- a/pkg/vere/pier.c +++ b/pkg/vere/pier.c @@ -3,6 +3,7 @@ #include "db/lmdb.h" #include "ent.h" #include "noun.h" +#include "pace.h" #include "vere.h" #include "version.h" @@ -674,8 +675,8 @@ _pier_wyrd_fail(u3_pier* pir_u, u3_ovum* egg_u, u3_noun lud) // XX organizing version constants // #define VERE_NAME "vere" -#define VERE_ZUSE 413 -#define VERE_LULL 324 +#define VERE_ZUSE 412 +#define VERE_LULL 323 /* _pier_wyrd_aver(): check for %wend effect and version downgrade. RETAIN */ @@ -807,12 +808,13 @@ _pier_wyrd_card(u3_pier* pir_u) // XX god_u not necessarily available yet, refactor call sites // - u3_noun ver = u3nt(u3i_string(VERE_NAME), + u3_noun ver = u3nq(u3i_string(VERE_NAME), + u3i_string(U3_VERE_PACE), u3dc("scot", c3__ta, u3i_string(URBIT_VERSION)), u3_nul); u3_noun kel = u3nl(u3nc(c3__zuse, VERE_ZUSE), // XX from both king and serf? u3nc(c3__lull, VERE_LULL), // XX from both king and serf? - u3nc(c3__arvo, 238), // XX from both king and serf? + u3nc(c3__arvo, 237), // XX from both king and serf? u3nc(c3__hoon, 139), // god_u->hon_y u3nc(c3__nock, 4), // god_u->noc_y u3_none); diff --git a/pkg/vere/vere.h b/pkg/vere/vere.h index 9561dedec1..cef16810b6 100644 --- a/pkg/vere/vere.h +++ b/pkg/vere/vere.h @@ -1231,6 +1231,13 @@ u3_auto* u3_behn_io_init(u3_pier* pir_u); + /** lick, IPC communication. + **/ + /* u3_lick_io_init(): initialize loch vane. + */ + u3_auto* + u3_lick_io_init(u3_pier* pir_u); + /** HTTP server. **/ /* u3_http_io_init(): initialize http I/O.