diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bcfe7233..50b7f4e0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ * directory mode sourcing dynamic sinks (sink-inbound only) * directory mode source-sink pairing 'reachable source' and 'tunnel' transfer modes * changed packaging format for appls + * net\_open("@stdin") can now be used to access a per-directory-appl messaging group ## Terminal * SGR reset fix, add CNL / CPL diff --git a/doc/net_discover.lua b/doc/net_discover.lua index 442cc9f64..bdca3cb52 100644 --- a/doc/net_discover.lua +++ b/doc/net_discover.lua @@ -64,9 +64,12 @@ -- the local keystore, while basename/subname is a breakdown of components in a -- fully qualified domain name. Multiple events may be used to provide a -- complete reverse entry: basename.basename.subname (com.example.local) and is --- terminated with an zero-length base- or subname. A12pub is a bit special in --- the sense that it provides the public key for a previously unknown a12 --- connection based on the TRUST_PERMIT_UNKNOWN trust model argument. +-- terminated with an zero-length base- or subname. +-- +-- If the *namespace* is a12pub, there is also a base64 coded form of the +-- public key presented as 'kpub'. This is useful when requesting to open that +-- as a source specifically where there might be ambiguity (directory) or a +-- change of petname (directory and broadcast discover). -- -- The (source, sink and directory) states describe the capabilities of the -- discovered node. If none of them are set, the capabilities could not be diff --git a/doc/net_open.lua b/doc/net_open.lua index e978823db..774fe4f85 100644 --- a/doc/net_open.lua +++ b/doc/net_open.lua @@ -6,8 +6,40 @@ -- speaking the a12 protocol. If *host* starts with an @ sign and matches a -- known name in the keystore, the connection information and authentication -- credentials will be picked from there. +-- -- The connection behaves just as if it had been initiated through -- ref:launch_target or or ref:target_alloc. +-- +-- If *host* starts with the reserved identifier @stdin it will try and +-- connect to an attached monitor and communicate through it with an external +-- directory server and the associated appl- group of others running the same +-- appl through the same directory. +-- +-- The restricted short (alnum _) identifier used server side is determined +-- first by the arcan-net --ident argument (or when opening the directory +-- through ref:net_discover) and, on collision, generated by the directory +-- server. The one actually used will be provided in a 'message' event as +-- 'a12:join=XXXXXX' where the Xs are substituted with the actual identity. +-- +-- Any higher level 'nickname' system is expected to be implemented as a nested +-- application specific protocol within the appl- message group. +-- +-- Should the connection be severed, an a12:disconnected will be issued, and +-- when if/it can be resumed, a12:reconnected. +-- +-- Message events received in this format must follow the argument packing in +-- builtin/string.lua:strings.unpack_shmif_argstr(src) (key=value with ':' as +-- separator, \t being substituted to ':'). They have quite short restrictions +-- (78b) and are not intended for large datastream serialisation since they can +-- have large amplification and trigger throttling in the processing chain. For +-- large transfers we have ref:open_nonblock or using the directory to create a +-- direct channel to a specific user. The server end will enforce a prefix of +-- from=name and reserve the use of an 'a12' key anywhere. It will reject +-- messages which attempts to use that. +-- +-- @note: with @stdin:user expect deliveries to have multipart, meaning they +-- need to be concatenated until a terminating multipart=false arrives. +-- -- @group: network -- @cfunction: net_open -- @related: net_discover, launch_target @@ -32,6 +64,19 @@ function main() end) #endif +#idef MAIN2 + local vid = net_open("@stdin:me", + function(source, status) + if status.kind == "message" then + + end + end + ) + if not valid_vid(vid) then + print("not connected to a directory server") + end +#endif + #ifdef ERROR1 #endif end diff --git a/src/a12/net/a12_helper_srv.c b/src/a12/net/a12_helper_srv.c index 157883171..4bf73bc94 100644 --- a/src/a12/net/a12_helper_srv.c +++ b/src/a12/net/a12_helper_srv.c @@ -28,6 +28,7 @@ struct shmifsrv_thread_data { struct a12_state* S; struct arcan_shmif_cont fake; struct a12helper_opts opts; + float font_sz; int kill_fd; uint8_t chid; diff --git a/src/a12/net/dir_cl.c b/src/a12/net/dir_cl.c index 669f931db..0a51bbaf3 100644 --- a/src/a12/net/dir_cl.c +++ b/src/a12/net/dir_cl.c @@ -184,6 +184,11 @@ static void on_cl_event( /* main use would be the appl- runner forwarding messages that direction */ a12int_trace(A12_TRACE_DIRECTORY, "event=%s", arcan_shmif_eventstr(ev, NULL, 0)); + if (ev->category == EVENT_EXTERNAL && + ev->ext.kind == EVENT_EXTERNAL_MESSAGE){ + arcan_shmif_enqueue(&I->shmif, ev); + return; + } /* we do have an ongoing transfer (--push-appl) that we wait for an OK or cancel * before marking that we're ready to shutdown */ @@ -327,10 +332,12 @@ static pid_t exec_cpath(struct a12_state* S, close(pstdin[0]); close(pstdin[1]); close(pstdout[0]); - close(STDERR_FILENO); +/* + * close(STDERR_FILENO); close(STDOUT_FILENO); open("/dev/null", O_WRONLY); open("/dev/null", O_WRONLY); +*/ execvp(ctx->bin, argv); exit(EXIT_FAILURE); } @@ -346,6 +353,34 @@ static pid_t exec_cpath(struct a12_state* S, return pid; } +static void runner_shmif(struct ioloop_shared* I) +{ + arcan_event ev; + + while (arcan_shmif_poll(&I->shmif, &ev) > 0){ + if (ev.category != EVENT_TARGET){ + continue; + } + + if (ev.tgt.kind != TARGET_COMMAND_MESSAGE) + continue; + +/* we need to flip the 'direction' as the other end expect us to behave like a + * shmif client, i.e. TARGET is from server to client, EXTERNAL is from client + */ + struct arcan_event out = { + .category = EVENT_EXTERNAL, + .ext.message.multipart = ev.tgt.ioevs[0].iv + }; + _Static_assert(sizeof(out.ext.message.data) == + sizeof(ev.tgt.message), "_event.h integrity"); + + memcpy(out.ext.message.data, ev.tgt.message, sizeof(out.ext.message.data)); + + a12_channel_enqueue(I->S, &out); + } +} + static void swap_appldir(const char* name, int basedir) { /* it is probably worth keeping track of the old here */ @@ -394,6 +429,59 @@ static void process_thread(struct ioloop_shared* I, bool ok) * defined override state. */ fprintf(A->pf_stdin, "continue\n"); } + +/* client wants to join the applgroup through a net_open call and has set up a + * connection point for us to access - this direction may seem a bit weird, but + * since we are on equal privilege and arcan-core does not have a way of + * hooking up a shmif_cont structure, only feeding it, this added the least + * amount of complexity across the chain. */ + else if (strncmp(buf, "join ", 5) == 0){ + if (I->shmif.addr){ + arcan_shmif_drop(&I->shmif); + } + + buf[strlen(buf)-1] = '\0'; + char cbuf[strlen(buf) + strlen(I->cbt->clopt->basedir_path) + 1]; + snprintf(cbuf, sizeof(cbuf), "%s/%s", I->cbt->clopt->basedir_path, &buf[5]); + +/* strip \n and connect */ + int dfd; + char* key = arcan_shmif_connect(cbuf, NULL, &dfd); + a12int_trace(A12_TRACE_DIRECTORY, + "appl_monitor:connect=%s:ok=%s", &buf[5], key ? "true":"false"); + if (!key){ + return; + } + + I->shmif = arcan_shmif_acquire(NULL, key, SEGID_MEDIA, 0); + I->shmif.epipe = dfd; + I->on_shmif = runner_shmif; + + if (!I->shmif.addr){ + a12int_trace(A12_TRACE_DIRECTORY, "appl_monitor:connect_fail"); + return; + } + + /* join the message group for the running appl */ + arcan_event ev = { + .category = EVENT_EXTERNAL, + .ext.kind = ARCAN_EVENT(IDENT) + }; + + size_t lim = sizeof(ev.ext.message.data)/sizeof(ev.ext.message.data[1]); + if (cbt->clopt->ident[0]){ + snprintf( + (char*)ev.ext.message.data, lim, "%d:%s", + I->cbt->clopt->applid, cbt->clopt->ident + ); + } + else + snprintf( + (char*)ev.ext.message.data, lim, "%d", I->cbt->clopt->applid); + a12_channel_enqueue(I->S, &ev); + arcan_shmif_resize(&I->shmif, 64, 64); + runner_shmif(I); + } } return; @@ -475,10 +563,24 @@ static void process_thread(struct ioloop_shared* I, bool ok) a12_enqueue_bstream(I->S, state_fd, A12_BTYPE_STATE, cbt->clopt->applid, false, state_sz, empty_ext); } - else if (!exec_res && !cbt->clopt->block_log){ - fprintf(stderr, "sending crash report (%zu) bytes\n", state_sz); - a12_enqueue_bstream(I->S, state_fd, - A12_BTYPE_CRASHDUMP, cbt->clopt->applid, false, state_sz, empty_ext); + else if (!exec_res){ + if (cbt->clopt->stderr_log){ + FILE* fpek = fdopen(state_fd, "r"); + while (!feof(fpek)){ + char buf[4096]; + size_t nr = fread(buf, 1, 4096, fpek); + if (nr) + fwrite(stderr, nr, 1, fpek); + } + fseek(fpek, 0, SEEK_SET); + fclose(fpek); + } + + if (!cbt->clopt->block_log){ + fprintf(stderr, "sending crash report (%zu) bytes\n", state_sz); + a12_enqueue_bstream(I->S, state_fd, + A12_BTYPE_CRASHDUMP, cbt->clopt->applid, false, state_sz, empty_ext); + } } out: diff --git a/src/a12/net/dir_srv.c b/src/a12/net/dir_srv.c index 2ba9c9087..a02cb51ad 100644 --- a/src/a12/net/dir_srv.c +++ b/src/a12/net/dir_srv.c @@ -41,6 +41,8 @@ struct dircl; struct dircl { int in_appl; + char identity[16]; + int type; bool pending_stream; @@ -53,6 +55,9 @@ struct dircl { uint8_t pubk[32]; bool authenticated; + char message_multipart[1024]; + size_t message_ofs; + struct shmifsrv_client* C; struct dircl* next; struct dircl* prev; @@ -83,10 +88,10 @@ static void rebuild_index(); * apply geographically appropriate blocklists for the inevitable censors */ static bool gotname(struct dircl* source, struct arcan_event ev) { - struct dircl* C = active_clients.root.next; bool rv = false; pthread_mutex_lock(&active_clients.sync); + struct dircl* C = active_clients.root.next; while (C){ if (C == source){ C = C->next; @@ -653,8 +658,61 @@ static void handle_bchunk_req(struct dircl* C, char* ext, bool input) }, -1); } +static void msgqueue_worker(struct dircl* C, arcan_event* ev) +{ + if (C->in_appl < 0) + return; + + char* str = (char*) ev->ext.message.data; + size_t len = strlen(str); + if (C->message_ofs + len >= sizeof(C->message_multipart)){ + A12INT_DIRTRACE("dirsv:kind=error:multipart_message_overflow:source=%s", C->identity); + return; + } + + memcpy( + &C->message_multipart[C->message_ofs], + str, + len + ); + + C->message_ofs += len; + +/* queue more */ + if (ev->ext.message.multipart) + return; + + C->message_multipart[C->message_ofs] = '\0'; + struct arcan_event outev = { + .category = EVENT_EXTERNAL, + .tgt.kind = EVENT_EXTERNAL_MESSAGE, + }; + +/* broadcast as one large chain so we don't risk any interleaving */ + pthread_mutex_lock(&active_clients.sync); + struct dircl* cur = active_clients.root.next; + while (cur){ + if (cur->in_appl == C->in_appl && cur != C){ + shmifsrv_enqueue_multipart_message( + cur->C, &outev, C->message_multipart, C->message_ofs); + } + cur = cur->next; + } + pthread_mutex_unlock(&active_clients.sync); + +/* reset the queue buffer */ + snprintf(C->message_multipart, 16, "from=%s:", C->identity); + C->message_ofs = strlen(C->message_multipart); +} + static void dircl_message(struct dircl* C, struct arcan_event ev) { +/* reserved prefix? then treat as worker command */ + if (strncmp((char*)ev.ext.message.data, "a12:", 4) != 0){ + msgqueue_worker(C, &ev); + return; + } + struct arg_arr* entry = arg_unpack((char*)ev.ext.message.data); if (!entry){ A12INT_DIRTRACE("dirsv:kind=worker:bad_msg:%s=", ev.ext.message.data); @@ -694,13 +752,9 @@ static void dircl_message(struct dircl* C, struct arcan_event ev) free(b64); shmifsrv_enqueue_event(C->C, &ev, -1); memcpy(C->pubk, pubk_dec, 32); - return; } - if (!arg_lookup(entry, "a12", 0, NULL)) - goto send_fail; - /* this one comes from a DIRLIST being sent to the worker state machine. The * worker doesn't retain a synched list and may flip between dynamic * notification and not. This results in a message being created in a12.c with @@ -753,6 +807,79 @@ void handle_netstate(struct dircl* C, arcan_event ev) A12INT_DIRTRACE("dirsv:kind=worker:unknown_netstate"); } +static bool got_collision(int appid, char* name) +{ + bool res = false; + pthread_mutex_lock(&active_clients.sync); + struct dircl* C = active_clients.root.next; + while (C){ + if (C->in_appl == appid){ + if (strcmp(name, C->identity) == 0){ + res = true; + break; + } + } + C = C->next; + } + pthread_mutex_unlock(&active_clients.sync); + return res; +} + +static void handle_ident(struct dircl* C, arcan_event ev) +{ + char* end; + +/* applind:uid - need to handle identifier collision and somehow notify the + * worker that we ack:ed the join but also that its actual identity flipped. We + * might also need to do this if moderation says anything. The easiest way + * around it would be a 'you are now' xyz. otoh - the presentation name should + * be implemented in some other way. The problem doesn't manifest in the first + * order as you don't need to 'see' your own identity, but if someone tries to + * reply to you or you reply to the colliding identity the problem will + * manifest. */ + size_t ind = strtoul((char*)ev.ext.message.data, &end, 10); + + char buf[10] = "anon_"; + if (*end == '\0' || (*(end+1)) == '\0'){ +make_random: + do { + uint8_t rnd[4]; + arcan_random(rnd, 4); + for (size_t i = 0; i < 4; i++){ + buf[i+5] = 'a' + (rnd[i] % 26); + } + end = buf; + } while (got_collision(ind, buf)); + } + else if (*end != ':'){ + A12INT_DIRTRACE("dirsv:kind=error:bad_join_id"); + return; + } + else if (*(++end)){ + size_t count = 0; + char* work = strdup(end); + + while (got_collision(ind, end)){ + count++; + if (count == 99) + goto make_random; + snprintf(end, 16, "%.13s_%d", work, count++); + } + + free(work); + } + +/* leave / join messages are sent by default, it is up to the application + * layer to determine if that is necessary - everything has magnification. */ + if (C->in_appl != -1){ + } + C->in_appl = ind; + + snprintf(C->identity, 16, "%s", end); + snprintf(C->message_multipart, 16, "from=%s:", C->identity); + C->message_ofs = strlen(C->message_multipart); +} + static void* dircl_process(void* P) { struct dircl* C = P; @@ -825,6 +952,7 @@ static void* dircl_process(void* P) /* petName for a source/dir or for joining an appl */ if (ev.ext.kind == EVENT_EXTERNAL_IDENT){ A12INT_DIRTRACE("dirsv:kind=worker:cl_join=%s", (char*)ev.ext.message.data); + handle_ident(C, ev); } else if (ev.ext.kind == EVENT_EXTERNAL_NETSTATE){ handle_netstate(C, ev); @@ -967,6 +1095,7 @@ void anet_directory_shmifsrv_thread( struct dircl* newent = malloc(sizeof(struct dircl)); *newent = (struct dircl){ .C = cl, + .in_appl = -1, .endpoint = { .category = EVENT_EXTERNAL, .ext.kind = EVENT_EXTERNAL_NETSTATE diff --git a/src/a12/net/dir_srv_worker.c b/src/a12/net/dir_srv_worker.c index bc0e28c41..288a468ff 100644 --- a/src/a12/net/dir_srv_worker.c +++ b/src/a12/net/dir_srv_worker.c @@ -192,6 +192,12 @@ static void on_a12srv_event( arcan_shmif_enqueue(C, &disc); } + else if (ev->ext.kind == EVENT_EXTERNAL_IDENT){ + a12int_trace(A12_TRACE_DIRECTORY, + "source_join=%s", ev->ext.message.data); + arcan_shmif_enqueue(C, ev); + } + /* Forward messages verbatim, this also latches into the dirlist command which * will trigger the server to re-synch dynamic sources, but it is a path to get * external (untrusted) messages to be parsed and should be treated as poison. */ @@ -340,16 +346,15 @@ static bool wait_for_activation( return false; } -static void do_event( +static void do_external_event( + struct directory_meta* cbt, struct a12_state* S, struct arcan_shmif_cont* C, struct arcan_event* ev) { - struct directory_meta* cbt = C->user; - -/* Parent process responsible for verifying and tagging name with petname:kpub. - * the NETSTATE associated with diropen is part of the on_directory event - * handler and doesn't reach this point. */ - if (ev->category == EVENT_EXTERNAL && - ev->ext.kind == EVENT_EXTERNAL_NETSTATE){ + switch (ev->ext.kind){ + case EVENT_EXTERNAL_MESSAGE: + a12_channel_enqueue(cbt->S, ev); + break; + case EVENT_EXTERNAL_NETSTATE:{ size_t i = 0; if (a12_remote_mode(S) == ROLE_SOURCE){ @@ -389,7 +394,22 @@ static void do_event( ev->ext.netstate.type, ev->ext.netstate.state != 0 ); } + break; + default: + } +} + +static void do_event( + struct a12_state* S, struct arcan_shmif_cont* C, struct arcan_event* ev) +{ + struct directory_meta* cbt = C->user; + + if (ev->category == EVENT_EXTERNAL) + return do_external_event(cbt, S, C, ev); +/* Parent process responsible for verifying and tagging name with petname:kpub. + * the NETSTATE associated with diropen is part of the on_directory event + * handler and doesn't reach this point. */ if (ev->category != EVENT_TARGET) return; @@ -615,9 +635,7 @@ void anet_directory_srv( SEGID_NETWORK_SERVER, SHMIF_ACQUIRE_FATALFAIL | SHMIF_NOACTIVATE | -#ifdef __OpenBSD__ SHMIF_DISABLE_GUARD | -#endif SHMIF_NOREGISTER, &args ); diff --git a/src/a12/net/dir_supp.c b/src/a12/net/dir_supp.c index 786628f54..8347eb9cd 100644 --- a/src/a12/net/dir_supp.c +++ b/src/a12/net/dir_supp.c @@ -32,12 +32,13 @@ void anet_directory_ioloop(struct ioloop_shared* I) { int errmask = POLLERR | POLLNVAL | POLLHUP; - struct pollfd fds[4] = + struct pollfd fds[5] = { {.fd = I->userfd, .events = POLLIN | errmask}, {.fd = I->fdin, .events = POLLIN | errmask}, {.fd = -1, .events = POLLOUT | errmask}, {.fd = -1, .events = POLLIN | errmask}, + {.fd = -1, .events = POLLIN | errmask}, }; uint8_t inbuf[9000]; @@ -53,7 +54,7 @@ void anet_directory_ioloop(struct ioloop_shared* I) fds[2].fd = I->fdout; /* regular simple processing loop, wait for DIRECTORY-LIST command */ - while (a12_ok(I->S) && -1 != poll(fds, 4, -1)){ + while (a12_ok(I->S) && -1 != poll(fds, 5, -1)){ if ((fds[0].revents | fds[1].revents | fds[2].revents | fds[3].revents) & errmask){ if (fds[0].revents & errmask){ I->on_userfd(I, false); @@ -61,10 +62,15 @@ void anet_directory_ioloop(struct ioloop_shared* I) break; } +/* this might add or remove a shmif to our tracking set */ if (fds[0].revents & POLLIN){ I->on_userfd(I, true); } + if (fds[4].revents){ + I->on_shmif(I); + } + if (fds[3].revents & POLLIN){ uint8_t buf[8832]; int fd = I->S->channels[1].unpack_state.bframe.tmp_fd; @@ -112,11 +118,13 @@ void anet_directory_ioloop(struct ioloop_shared* I) } fds[0].revents = fds[1].revents = fds[2].revents = fds[3].revents = 0; + fds[4].revents = 0; fds[2].fd = outbuf_sz ? I->fdout : -1; fds[0].fd = I->userfd; fds[3].fd = I->S->channels[1].unpack_state.bframe.tunnel ? I->S->channels[1].unpack_state.bframe.tmp_fd : -1; + fds[4].fd = I->shmif.addr ? I->shmif.epipe : -1; } } diff --git a/src/a12/net/directory.h b/src/a12/net/directory.h index 7e9433f9d..ca5bb8339 100644 --- a/src/a12/net/directory.h +++ b/src/a12/net/directory.h @@ -45,6 +45,7 @@ struct anet_dircl_opts { bool reload; bool block_state; bool block_log; + bool stderr_log; bool keep_appl; bool request_tunnel; @@ -117,6 +118,9 @@ struct ioloop_shared { int fdout; int userfd; + struct arcan_shmif_cont shmif; + int shmiffd; + pthread_mutex_t lock; struct a12_state *S; volatile bool shutdown; @@ -129,6 +133,7 @@ struct ioloop_shared { bool (*on_directory)(struct ioloop_shared* S, struct appl_meta* dir); void (*on_userfd)(struct ioloop_shared* S, bool ok); + void (*on_shmif)(struct ioloop_shared* S); void* tag; }; diff --git a/src/a12/net/net.c b/src/a12/net/net.c index 4a0e71247..cfa026be0 100644 --- a/src/a12/net/net.c +++ b/src/a12/net/net.c @@ -918,6 +918,7 @@ static bool show_usage(const char* msg) "\t --push-appl s \t Push [s] from APPLBASE to the server\n" "\t --tunnel \t Default request tunnelling as source/sink connection\n" "\t --block-log \t Don't attempt to forward script errors or crash logs\n" + "\t --stderr-log \t Mirror script errors / crash log to stderr\n" "\t --source-port \t When sourcing use this port for listening\n" "\t --block-state \t Don't attempt to synch state before/after running appl\n\n" "Directory server options: \n" @@ -1206,6 +1207,9 @@ static int apply_commandline(int argc, char** argv, struct arcan_net_meta* meta) else if (strcmp(argv[i], "--block-log") == 0){ global.dircl.block_log = true; } + else if (strcmp(argv[i], "--stderr-log") == 0){ + global.dircl.stderr_log = true; + } else if (strcmp(argv[i], "--block-state") == 0){ global.dircl.block_state = true; } diff --git a/src/engine/arcan_lua.c b/src/engine/arcan_lua.c index b29a5439f..47e5ec325 100644 --- a/src/engine/arcan_lua.c +++ b/src/engine/arcan_lua.c @@ -4309,7 +4309,7 @@ static char* spacetostr(int space) case 4: return "ipv6"; break; - case 6: + case 5: return "a12pub"; break; default: @@ -4545,9 +4545,14 @@ bool arcan_lua_pushevent(lua_State* ctx, arcan_event* ev) break; case EVENT_EXTERNAL_NETSTATE: tbldynstr(ctx, "kind", "state", top); - MSGBUF_UTF8(ev->ext.netstate.name); - tbldynstr(ctx, "name", msgbuf, top); tbldynstr(ctx, "namespace", spacetostr(ev->ext.netstate.space), top); + if (ev->ext.netstate.space == 5){ + + } + else { + MSGBUF_UTF8(ev->ext.netstate.name); + tbldynstr(ctx, "name", msgbuf, top); + } if (ev->ext.netstate.state & (1 | 2 | 4)){ tblbool(ctx, "discovered", true, top); @@ -4558,13 +4563,12 @@ bool arcan_lua_pushevent(lua_State* ctx, arcan_event* ev) else tblbool(ctx, "bad", true, top); - if (ev->ext.netstate.type & 1) + if (ev->ext.netstate.type == 1) tblbool(ctx, "source", true, top); - if (ev->ext.netstate.type & 2) + if (ev->ext.netstate.type == 2) tblbool(ctx, "sink", true, top); - if (ev->ext.netstate.type & 4) + if (ev->ext.netstate.type == 4) tblbool(ctx, "directory", true, top); - break; case EVENT_EXTERNAL_REGISTER:{ /* prevent switching types */ @@ -11674,9 +11678,39 @@ static int net_open(lua_State* ctx) char* host = strdup(luaL_checkstring(ctx, 1)); intptr_t ref = find_lua_callback(ctx); - if (strcmp(host, "@stdin") == 0){ - arcan_vobj_id vid = arcan_monitor_fsrvvid(ref); - lua_pushvid(ctx, vid); +/* + * generate a connection point for the outer monitor to attach to (in order for + * the types to work), with the parts after : being used to forward identity. + * A more open question is if this should be recursive, if we net-open an appl + * over a directory and embed into ourselves, should the communication be routed + * through the chain or be treated parallel? + */ + if (strncmp(host, "@stdin", 6) == 0){ + uint8_t rnd[6]; + char co[13]; + arcan_random(rnd, 6); + for (size_t i = 0; i < sizeof(rnd); i++){ + co[i*2] = "0123456789abcdef"[rnd[i] >> 4]; + co[i*2+1] = "0123456789abcdef"[rnd[i] & 0x0f]; + } + co[12] = '\0'; + + arcan_frameserver* newref = + platform_launch_listen_external(co, NULL, -1, ARCAN_SHM_UMASK, 32, 32, ref); + if (!newref){ + arcan_warning("couldn't listen on connection point (%s)\n", co); + lua_pushvid(ctx, ARCAN_EID); + free(host); + return 1; + } + + arcan_conductor_register_frameserver(newref); + arcan_monitor_fsrvvid(co); + trace_allocation(ctx, "net_listen", newref->vid); + +/* only different thing to regular frameserver setup is that the monitor need to + * forward the connection information through its established channel. */ + lua_pushvid(ctx, newref->vid); free(host); return 1; } diff --git a/src/engine/arcan_monitor.c b/src/engine/arcan_monitor.c index 65248ecce..b4b459c3b 100644 --- a/src/engine/arcan_monitor.c +++ b/src/engine/arcan_monitor.c @@ -1,5 +1,7 @@ #include "arcan_hmeta.h" + extern struct arcan_luactx* main_lua_context; +extern volatile _Atomic int main_lua_signalled; static int m_srate; static int m_ctr; @@ -9,6 +11,8 @@ static bool m_locked; static bool m_transaction; static int longjmp_mode; +static struct arcan_shmif_cont* group; + /* * instead of adding more commands here, the saner option is to establish * a shmif based control interface (with the interesting consequence of @@ -116,6 +120,10 @@ static void cmd_continue(char* arg) cmd_commit(arg); } +static void cmd_shmif(char* arg) +{ +} + void arcan_monitor_watchdog(lua_State* L, lua_Debug* D) { /* triggered on SIGUSR1 - used by m_ctrl to indicate that @@ -137,7 +145,8 @@ void arcan_monitor_watchdog(lua_State* L, lua_Debug* D) {"loadkey", cmd_loadkey}, {"commit", cmd_commit}, {"reload", cmd_reload}, - {"lock", cmd_lock} + {"lock", cmd_lock}, + {"shmif", cmd_shmif}, }; m_locked = true; @@ -182,6 +191,7 @@ bool arcan_monitor_configure(int srate, const char* dst, FILE* ctrl) bool logfdtgt = dst ? strncmp(dst, "LOGFD:", 6) == 0 : false; m_ctrl = ctrl; + setlinebuf(m_ctrl); if (!logtgt && !logfdtgt) return false; @@ -199,7 +209,7 @@ bool arcan_monitor_configure(int srate, const char* dst, FILE* ctrl) fcntl(fd, F_SETFD, FD_CLOEXEC); } } - + setlinebuf(m_out); return true; } @@ -260,7 +270,11 @@ void arcan_monitor_tick() arcan_lua_statesnap(m_out, buf, true); } -arcan_vobj_id arcan_monitor_fsrvvid(intptr_t ref) +bool arcan_monitor_fsrvvid(const char* cp) { - return ARCAN_EID; + if (!m_ctrl) + return false; + + fprintf(m_out, "join %s\n", cp); + return true; } diff --git a/src/engine/arcan_monitor.h b/src/engine/arcan_monitor.h index 3ac6a66bd..d347f57b8 100644 --- a/src/engine/arcan_monitor.h +++ b/src/engine/arcan_monitor.h @@ -49,9 +49,9 @@ void arcan_monitor_tick(); void arcan_monitor_finish(bool ok); /* - * get a vobj id bound to ref handler for mapping the monitor connection - * to the net api + * forward connection point information over the monitor and have the + * parent bind to it (replacing any existing one). */ -arcan_vobj_id arcan_monitor_fsrvvid(intptr_t ref); +bool arcan_monitor_fsrvvid(const char* cp); #endif diff --git a/src/frameserver/net/default/net.c b/src/frameserver/net/default/net.c index a49e98aa1..7a675fd47 100644 --- a/src/frameserver/net/default/net.c +++ b/src/frameserver/net/default/net.c @@ -563,8 +563,19 @@ static void cl_got_dyn(struct a12_state* S, int type, const char* petname, bool found, uint8_t pubk[static 32], void* tag) { struct arcan_shmif_cont* C = arcan_shmif_primary(SHMIF_INPUT); -/* convert back to NETSTATE and send to arcan */ -/* remember petname and pubk pairing */ + arcan_event disc = { + .category = EVENT_EXTERNAL, + .ext.kind = EVENT_EXTERNAL_NETSTATE, + .ext.netstate = { + .type = type, + .space = 5, + .state = found + } + }; + snprintf(disc.ext.netstate.petname, 16, "%s", petname); + memcpy(disc.ext.netstate.pubk, pubk, 32); + + arcan_shmif_enqueue(C, &disc); } /* returning false here would break us out of the ioloop */ diff --git a/src/shmif/arcan_shmif_event.h b/src/shmif/arcan_shmif_event.h index 326e0f618..a759210c9 100644 --- a/src/shmif/arcan_shmif_event.h +++ b/src/shmif/arcan_shmif_event.h @@ -1479,7 +1479,13 @@ enum ARCAN_TARGET_SKIPMODE { * 4 = directory */ struct { - char name[66]; /* covers local tag, chunked hostname, ipv6 string, ... */ + union { + char name[66]; /* covers local tag, chunked hostname, ipv6 string, ... */ + struct { + char petname[16]; + uint8_t pubk[32]; + }; + }; uint8_t space; uint8_t state; uint8_t type; diff --git a/src/shmif/arcan_shmif_server.c b/src/shmif/arcan_shmif_server.c index 9b7775bf7..a8d74c175 100644 --- a/src/shmif/arcan_shmif_server.c +++ b/src/shmif/arcan_shmif_server.c @@ -673,3 +673,64 @@ void shmifsrv_monotonic_rebase() timebase = arcan_timemillis(); c_ticks = 0; } + +#include "../frameserver/util/utf8.c" +bool shmifsrv_enqueue_multipart_message(struct shmifsrv_client* acon, + struct arcan_event* base, const char* msg, size_t len) +{ + uint32_t state = 0, codepoint = 0; + uint8_t* multipart; + char* data; + +/* different offsets for different categories, we need to be able to + * handle both directions hence why _server works differently than _control.c */ + if (base->category == EVENT_TARGET){ + data = (char*)base->tgt.message; + multipart = &base->tgt.ioevs[0].cv[0]; /* works because multipart is !0 */ + } + else if (base->category == EVENT_EXTERNAL){ + multipart = &base->ext.message.multipart; + data = (char*)base->ext.message.data; + } + else + return false; + + _Static_assert(sizeof(base->ext.message.data) == 78, "broken header"); + size_t maxlen = 78; + const char* outs = msg; + +/* utf8- point aligned against block size */ + while (len > maxlen){ + size_t i, lastok = 0; + state = 0; + for (i = 0; i <= maxlen - 1; i++){ + if (UTF8_ACCEPT == utf8_decode(&state, &codepoint, (uint8_t)(msg[i]))) + lastok = i; + + if (i != lastok){ + if (0 == i) + return false; + } + } + + memcpy(data, outs, lastok); + data[lastok] = '\0'; + len -= lastok; + outs += lastok; + if (len) + *multipart = 1; + else + *multipart = 0; + + platform_fsrv_pushevent(acon->con, base); + } + +/* flush remaining */ + if (len){ + snprintf(data, maxlen, "%s", outs); + *multipart = 0; + platform_fsrv_pushevent(acon->con, base); + } + + return true; +} diff --git a/src/shmif/arcan_shmif_server.h b/src/shmif/arcan_shmif_server.h index 879e984cf..42fad0841 100644 --- a/src/shmif/arcan_shmif_server.h +++ b/src/shmif/arcan_shmif_server.h @@ -92,13 +92,6 @@ enum shmifsrv_status { bool shmifsrv_enter(struct shmifsrv_client*); void shmifsrv_leave(); -/* - * Wrap a shmifsrv client into an interface that can be used with the normal - * arcan_shmif_... class of functions. - */ -struct arcan_shmif_cont* - shmifsrv_client_wrap(struct shmifsrv_client* srv); - /* * Allocate, prepare and transfer a new sub-segment to the frameserver * referenced by [dst] (!NULL). @@ -225,6 +218,12 @@ void shmifsrv_free(struct shmifsrv_client*, int mode); bool shmifsrv_enqueue_event( struct shmifsrv_client*, struct arcan_event*, int fd); +/* + * Split up a longer message into a multipart set of message events + */ +bool shmifsrv_enqueue_multipart_message( + struct shmifsrv_client*, struct arcan_event*, const char*, size_t); + /* * [CRITICAL] * Attempt to dequeue up to [limit] events from the ingoing event queue. Will diff --git a/src/tools/aloadimage/aloadimage.c b/src/tools/aloadimage/aloadimage.c index 8b1a4967b..71bebf0f8 100644 --- a/src/tools/aloadimage/aloadimage.c +++ b/src/tools/aloadimage/aloadimage.c @@ -613,7 +613,8 @@ static bool dispatch_event( break; case TARGET_COMMAND_STEPFRAME: if (ev->tgt.ioevs[1].iv == 0xfeed) - poll_pl(ds, 1); + if (poll_pl(ds, 1)) + return true; if (ds->step_timer > 0 && !ds->step_block){ if (ds->step_timer == 0){