jcs
/subtext
/amendments
/17
session: Support view templates
Properly close sessions in telnet+console
jcs made amendment 17 over 2 years ago
--- console.c Sun Dec 5 21:43:35 2021
+++ console.c Tue Dec 7 15:02:14 2021
@@ -29,13 +29,14 @@
struct node_funcs console_node_funcs = {
console_input,
console_output,
+ console_close
};
void console_redraw(struct console *console, short numhints, short *hints);
void console_parse_csi(struct console *console);
struct console *
-console_init(void)
+console_init(struct console **cur_console)
{
char title[64] = { 0 };
struct console *console;
@@ -44,6 +45,7 @@ console_init(void)
short width, height;
console = xmalloczero(sizeof(struct console));
+ console->cur_console = cur_console;
memset(console->chars, ' ', sizeof(console->chars));
console->ncolumns = 80;
console->nlines = 24;
@@ -80,76 +82,31 @@ console_init(void)
console->session = session_create("console", &console_node_funcs);
console->session->cookie = (void *)console;
+ *cur_console = console;
+
return console;
}
void
-console_idle(struct console *console)
+console_close(struct session *session)
{
- console_output(console->session);
-}
+ struct console *console = (struct console *)session->cookie;
-void
-console_suspend(struct console *console)
-{
+ DisposeWindow(console->win);
+ *(console->cur_console) = NULL;
+ free(console);
}
void
-console_resume(struct console *console)
+console_idle(struct console *console)
{
-}
-
-void
-console_update(struct console *console, EventRecord *event)
-{
- Rect r;
- short what = -1;
-
- if (event != NULL)
- what = event->what;
-
- switch (what) {
- case -1:
- case updateEvt:
- console_redraw(console, -1, NULL);
- //browser_update_menu(browser);
- UpdtControl(console->win, console->win->visRgn);
-
- break;
- case activateEvt:
- break;
- }
-}
-
-void
-console_mouse_down(struct console *console, EventRecord *event)
-{
-}
-
-void
-console_key_down(struct console *console, EventRecord *event)
-{
- char k;
-
- if (console->session->ibuflen >= sizeof(console->session->ibuf))
- return;
-
- k = (event->message & charCodeMask);
- console->session->ibuf[console->session->ibuflen++] = k;
- if (k == '\r')
- console->session->ibuf[console->session->ibuflen++] = '\n';
-}
-
-short
-console_output(struct session *session)
-{
- struct console *console = (struct console *)session->cookie;
+ struct session *session = console->session;
short n, len, cursor, iac = 0;
short hints[100];
short nhints = 0;
if (session->obuflen == 0)
- return 0;
+ return;
cursor = (console->cursor_line * console->ncolumns) +
console->cursor_column;
@@ -166,7 +123,7 @@ console_output(struct session *session)
if (console->in_csi) {
if (session->obuf[n] == '\e' ||
- console->csilen >= sizeof(console->csi) - 1) {
+ console->csilen >= nitems(console->csi) - 1) {
console_parse_csi(console);
console->in_csi = 0;
console->csi[0] = '\0';
@@ -206,7 +163,7 @@ console_output(struct session *session)
console->chars[cursor] = session->obuf[n];
console->attrs[cursor] |= ATTR_DIRTY;
console->cursor_column++;
- if (nhints != -1 && nhints < sizeof(hints) - 1)
+ if (nhints != -1 && nhints < nitems(hints) - 1)
hints[nhints++] = cursor;
else
nhints = -1;
@@ -214,7 +171,6 @@ console_output(struct session *session)
}
}
-
len = session->obuflen;
session->obuflen = 0;
@@ -222,29 +178,85 @@ output_done:
cursor = (console->cursor_line * console->ncolumns) +
console->cursor_column;
console->attrs[cursor] |= (ATTR_DIRTY | ATTR_CURSOR);
- if (nhints != -1 && nhints < sizeof(hints) - 1)
+ if (nhints != -1 && nhints < nitems(hints) - 1)
hints[nhints++] = cursor;
else if (nhints == -1)
nhints = 0;
console_redraw(console, nhints, hints);
+}
+
+void
+console_suspend(struct console *console)
+{
+}
+
+void
+console_resume(struct console *console)
+{
+}
+
+void
+console_update(struct console *console, EventRecord *event)
+{
+ Rect r;
+ short what = -1;
- return len;
+ if (event != NULL)
+ what = event->what;
+
+ switch (what) {
+ case -1:
+ case updateEvt:
+ console_redraw(console, -1, NULL);
+ //browser_update_menu(browser);
+ UpdtControl(console->win, console->win->visRgn);
+
+ break;
+ case activateEvt:
+ break;
+ }
}
+void
+console_mouse_down(struct console *console, EventRecord *event)
+{
+}
+
+void
+console_key_down(struct console *console, EventRecord *event)
+{
+ char k;
+
+ if (console->session->ibuflen >= nitems(console->session->ibuf))
+ return;
+
+ k = (event->message & charCodeMask);
+ console->session->ibuf[console->session->ibuflen++] = k;
+ if (k == '\r')
+ console->session->ibuf[console->session->ibuflen++] = '\n';
+}
+
short
+console_output(struct session *session)
+{
+ /* nothing to do here, output is printed from console_idle */
+ uthread_yield();
+ return 0;
+}
+
+short
console_input(struct session *session)
{
- /* nothing to do, input is fed from main loop */
+ /* nothing to do here, input is fed from main loop */
uthread_yield();
return 0;
}
void
-console_redraw(struct console *console, short nhints, short hints[])
+console_redraw(struct console *console, short nhints, short *hints)
{
Rect cursor;
- short n, cell, line = 0, column = 0;
- short nsize;
+ short n, nsize, cell, line = 0, column = 0;
TextFont(monaco);
TextSize(9);
@@ -252,7 +264,7 @@ console_redraw(struct console *console, short nhints,
if (nhints > 0)
nsize = nhints;
else {
- nsize = sizeof(console->chars);
+ nsize = nitems(console->chars);
SetCursor(*(GetCursor(watchCursor)));
}
@@ -343,7 +355,7 @@ console_parse_csi(struct console *console)
param1 = 1;
if (console->csilen > 1) {
- for (x = 0; x < console->csilen - 1, x < sizeof(parambuf); x++) {
+ for (x = 0; x < console->csilen - 1, x < nitems(parambuf); x++) {
parambuf[x] = console->csi[x];
parambuf[x + 1] = '\0';
}
@@ -619,24 +631,8 @@ console_parse_csi(struct console *console)
session_output(console->session, "\e[0n");
break;
case 6: /* CPR - report cursor position */
- session_output(console->session, "\e[");
-
- sprintf(parambuf, "%d", console->cursor_line + 1);
- for (x = 0; x < sizeof(parambuf); x++) {
- if (parambuf[x] == '\0')
- break;
- session_output_char(console->session, parambuf[x]);
- }
- session_output_char(console->session, ';');
-
- sprintf(parambuf, "%d", console->cursor_column + 1);
- for (x = 0; x < sizeof(parambuf); x++) {
- if (parambuf[x] == '\0')
- break;
- session_output_char(console->session, parambuf[x]);
- }
-
- session_output_char(console->session, 'R');
+ session_output(console->session, "\e[%d;%dR",
+ console->cursor_line + 1, console->cursor_column + 1);
break;
}
break;
--- console.h Sun Nov 28 13:22:53 2021
+++ console.h Mon Dec 6 17:32:18 2021
@@ -21,6 +21,7 @@
struct console {
short state;
+ struct console **cur_console;
WindowPtr win;
ControlHandle scroller;
char chars[80 * 24];
@@ -41,7 +42,7 @@ struct console {
struct session *session;
};
-struct console *console_init(void);
+struct console *console_init(struct console **cur_console);
void console_idle(struct console *console);
void console_suspend(struct console *console);
void console_resume(struct console *console);
@@ -49,6 +50,7 @@ void console_update(struct console *console, EventReco
void console_mouse_down(struct console *console, EventRecord *event);
void console_key_down(struct console *console, EventRecord *event);
+void console_close(struct session *session);
short console_output(struct session *session);
short console_input(struct session *session);
short console_read(struct session *session);
--- db.c Mon Dec 6 09:26:36 2021
+++ db.c Mon Dec 6 14:25:11 2021
@@ -168,7 +168,7 @@ db_init(char *path, short is_new)
tdb = xmalloczero(sizeof(struct db));
tdb->fh = fh;
- memcpy(tdb->filename, path, strlen(path) + 1);
+ strlcpy(tdb->filename, path, sizeof(tdb->filename));
if (db_migrate(tdb, is_new) != 0) {
free(tdb);
--- db.h Sun Dec 5 22:59:33 2021
+++ db.h Mon Dec 6 10:35:10 2021
@@ -25,16 +25,21 @@
#define DB_TYPE 'STDB'
+#define DB_CUR_VERS 1
+
+#define DB_TRUE 0x100
+#define DB_FALSE 0x000
+
#define DB_CONFIG_RTYPE 'STCF'
#define DB_USER_RTYPE 'STUS'
#define DB_BOARD_RTYPE 'STBD'
#define DB_FILEAREA_RTYPE 'STFL'
#define DB_VERS_RTYPE 'STVR'
-#define DB_CUR_VERS 1
-
-#define DB_TRUE 0x100
-#define DB_FALSE 0x000
+#define DB_TEXT_TYPE 'TEXT'
+#define DB_TEXT_MENU_ID 1
+#define DB_TEXT_SHORTMENU_ID 2
+#define DB_TEXT_ISSUE_ID 3
struct config {
char name[32];
--- main.c Mon Dec 6 09:45:14 2021
+++ main.c Tue Dec 7 15:08:48 2021
@@ -45,12 +45,10 @@ main(void)
AppFile finder_file;
short event_in, n, finder_action, finder_count;
char *dbpath;
- char key;
+ char key, iters;
uthread_init();
- mainResFile = CurMap;
-
InitGraf(&thePort);
InitFonts();
FlushEvents(everyEvent, 0);
@@ -61,6 +59,7 @@ main(void)
InitCursor();
MaxApplZone();
+ mainResFile = CurMap;
err_init();
load_db_tmpls();
@@ -87,18 +86,23 @@ main(void)
if (!db)
ExitToShell();
- cur_console = console_init();
+ console_init(&cur_console);
if (db->config.telnet_port)
telnet_init();
+ iters = 0;
while (!quitting) {
if (db->config.telnet_port)
telnet_idle();
uthread_coordinate();
- WaitNextEvent(everyEvent, &event, 1, 0);
-
+ if (++iters % 10 == 0) {
+ WaitNextEvent(everyEvent, &event, 1, 0);
+ iters = 0;
+ } else if (!GetNextEvent(everyEvent, &event))
+ continue;
+
switch (event.what) {
case nullEvent:
if (cur_console)
--- session.c Mon Dec 6 10:01:19 2021
+++ session.c Tue Dec 7 13:20:38 2021
@@ -46,7 +46,7 @@ session_create(char *node, struct node_funcs *node_fun
session->state = SESSION_STATE_INIT;
session->node_funcs = node_funcs;
- memcpy(session->node, node, sizeof(session->node));
+ strlcpy(session->node, node, sizeof(session->node));
nsessions++;
sessions = xreallocarray(sessions, nsessions, sizeof(struct session));
@@ -59,26 +59,68 @@ void
session_run(struct uthread *uthread, void *arg)
{
struct session *s = (struct session *)arg;
+ Handle h;
+ char *view;
+ size_t len;
- session_output(s, "\r\n"
- "Welcome to %s (%s)\r\n"
- "\r\n", db->config.name, s->node);
+ len = session_load_view(s, DB_TEXT_ISSUE_ID, &view);
+ if (len) {
+ session_output_formatted(s, view, len);
+ free(view);
+ } else {
+ session_output(s, "\r\n"
+ "Welcome to %s (%s)\r\n"
+ "\r\n", db->config.name, s->node);
+ }
if (session_login(s) == AUTH_USER_OK)
session_output(s, "Welcome, %s\r\n", s->user->username);
else
session_output(s, "Thanks for playing\r\n");
- for (;;) {
+ session_close(&s);
+}
+
+void
+session_close(struct session **session)
+{
+ struct session **newsessions = NULL;
+ unsigned long now;
+ short onsessions, n;
+
+ /* try to flush output */
+ now = Ticks;
+ while ((*session)->obuflen && (Ticks - now) < 60) {
+ (*session)->node_funcs->output(*session);
uthread_yield();
}
+
+ /* close the node */
+ (*session)->node_funcs->close(*session);
+
+ /* remove session from sessions */
+ onsessions = nsessions - 1;
+ if (onsessions) {
+ newsessions = xmallocarray(onsessions, sizeof(struct session));
+ nsessions = 0;
+ for (n = 0; n < onsessions; n++) {
+ if (sessions[n] == *session)
+ continue;
+ newsessions[nsessions++] = sessions[n];
+ }
+ }
+ free(sessions);
+ sessions = newsessions;
+
+ free(*session);
+ *session = NULL;
}
-short
+size_t
session_output(struct session *session, const char *format, ...)
{
va_list ap;
- short len;
+ size_t len;
va_start(ap, format);
len = vsprintf(session_tbuf, format, ap);
@@ -86,25 +128,36 @@ session_output(struct session *session, const char *fo
if (len >= sizeof(session_tbuf))
err(1, "sprintf overflow in session_output!");
- if (len > (sizeof(session->obuf) - session->obuflen)) {
- warn("session_output is out of room!");
- len = sizeof(session->obuf) - session->obuflen;
- }
-
- memcpy(session->obuf + session->obuflen, session_tbuf, len);
- session->obuflen += len;
- session->node_funcs->output(session);
-
- return len;
+ return session_output_formatted(session, session_tbuf, len);
}
-short
-session_output_char(struct session *session, const char c)
+size_t
+session_output_formatted(struct session *session, const char *str,
+ size_t len)
{
- session->obuf[session->obuflen++] = c;
- session->node_funcs->output(session);
+ size_t chunk, olen = len, stroff = 0;
- return 1;
+ while (len) {
+ chunk = (sizeof(session->obuf) - session->obuflen);
+ if (chunk == 0) {
+ /* wait until output buffer clears */
+ uthread_yield();
+ continue;
+ }
+ if (chunk > len)
+ chunk = len;
+
+ memcpy(session->obuf + session->obuflen, str + stroff, chunk);
+ session->obuflen += chunk;
+ stroff += chunk;
+ len -= chunk;
+ session->node_funcs->output(session);
+
+ if (len)
+ uthread_yield();
+ }
+
+ return olen;
}
char
@@ -177,7 +230,7 @@ session_field_input(struct session *session, unsigned
ipos++;
ilen++;
field[ipos] = '\0';
- session_output_char(session, mask ? mask : c);
+ session_output_formatted(session, mask ? &mask : &c, 1);
}
}
@@ -231,9 +284,69 @@ session_login(struct session *s)
if (s->user)
return AUTH_USER_OK;
- uthread_sleep(60);
+ uthread_msleep(60);
session_output(s, "Login incorrect\r\n");
}
return AUTH_USER_FAILED;
+}
+
+#define EXPAND_TO_FIT(var, size, pos, len) { \
+ if ((pos) >= ((size) - (len))) { \
+ (size) += 1024; \
+ (var) = xrealloc((var), (size)); \
+ } \
+}
+
+size_t
+session_load_view(struct session *session, short id, char **ret)
+{
+ char curvar[64];
+ size_t retsize = 1024, retpos = 0;
+ size_t hsize, len;
+ Handle h;
+ short n, invar = 0, varlen = 0;
+ char c;
+
+ h = Get1Resource(DB_TEXT_TYPE, id);
+ if (!h)
+ return 0;
+ hsize = GetHandleSize(h);
+
+ *ret = xmalloc(retsize);
+ retpos = 0;
+ HLock(h);
+
+ for (n = 0; n < hsize; n++) {
+ c = (*h)[n];
+
+ if (c == '%' && (*h)[n + 1] == '%') {
+ n++;
+ if (invar) {
+ invar = 0;
+ curvar[varlen] = '\0';
+ if (strcmp(curvar, "NODE") == 0) {
+ len = strlcpy(curvar, session->node, sizeof(curvar));
+ EXPAND_TO_FIT(*ret, retsize, retpos, len);
+ memcpy(*ret + retpos, curvar, len);
+ retpos += len;
+ }
+ } else {
+ invar = 1;
+ varlen = 0;
+ }
+ } else if (invar) {
+ if (varlen < sizeof(curvar) - 2)
+ curvar[varlen++] = c;
+ } else if (c == '\r' && (*h)[n + 1] != '\n') {
+ EXPAND_TO_FIT(*ret, retsize, retpos, 2);
+ (*ret)[retpos++] = '\r';
+ (*ret)[retpos++] = '\n';
+ } else {
+ EXPAND_TO_FIT(*ret, retsize, retpos, 1);
+ (*ret)[retpos++] = c;
+ }
+ }
+
+ return retpos;
}
--- session.h Sun Dec 5 16:50:30 2021
+++ session.h Tue Dec 7 08:40:19 2021
@@ -34,11 +34,12 @@ enum session_input_state {
struct node_funcs {
short (*input)(struct session *session);
short (*output)(struct session *session);
+ void (*close)(struct session *session);
};
struct session {
- char node[32];
- char obuf[64];
+ char node[10];
+ char obuf[512];
char ibuf[64];
enum session_state state;
enum session_input_state input_state;
@@ -54,11 +55,14 @@ extern struct session **sessions;
extern short nsessions;
struct session *session_create(char *node, struct node_funcs *node_funcs);
+void session_close(struct session **session);
void session_idle(struct session *session);
char session_input_char(struct session *session);
-short session_output(struct session *session, const char *format, ...);
-short session_output_char(struct session *session, const char c);
+size_t session_output(struct session *session, const char *format, ...);
+size_t session_output_formatted(struct session *session, const char *str,
+ size_t len);
char *session_field_input(struct session *session, unsigned short len,
char mask);
+size_t session_load_view(struct session *session, short id, char **ret);
#endif /* __SESSION_H__ */
--- telnet.c Mon Dec 6 09:44:44 2021
+++ telnet.c Tue Dec 7 15:41:49 2021
@@ -47,12 +47,11 @@ TCPiopb telnet_exit_pb;
TCPStatusPB telnet_status_pb;
void telnet_atexit(void);
-short telnet_output(struct session *session);
-short telnet_input(struct session *session);
struct node_funcs telnet_node_funcs = {
telnet_input,
telnet_output,
+ telnet_close
};
void
@@ -60,9 +59,8 @@ telnet_init(void)
{
struct telnet_node *node;
short error, i;
- ip_port port = db->config.telnet_port;
- if (!port)
+ if (!db->config.telnet_port)
return;
if (_TCPInit() != 0)
@@ -80,17 +78,7 @@ telnet_init(void)
node->tcp_buf_len = (4 * 1500) + 1024;
node->tcp_buf = xmalloc(node->tcp_buf_len);
- error = _TCPCreate(&node->pb, &node->stream, (Ptr)node->tcp_buf,
- node->tcp_buf_len, nil, nil, nil, false);
- if (error)
- err(1, "TCPCreate[%d] failed: %d", i, error);
-
- error = _TCPPassiveOpen(&node->listen_pb, node->stream, nil, nil,
- nil, &port, nil, nil, true);
- if (error)
- err(1, "TCPPassiveOpen[%d] on port %d failed: %d", i, port,
- error);
- node->state = TELNET_PB_STATE_LISTENING;
+ /* delay actual listening until telnet_idle */
}
}
@@ -104,15 +92,14 @@ telnet_atexit(void)
node = &telnet_nodes[i];
if (node->state > TELNET_PB_STATE_UNUSED) {
- error = _TCPClose(&telnet_exit_pb, node->stream, nil, nil,
+ error = _TCPAbort(&telnet_exit_pb, node->stream, nil, nil,
false);
- if (error == noErr)
- error = _TCPRelease(&telnet_exit_pb, node->stream, nil,
- nil, false);
+ error = _TCPRelease(&telnet_exit_pb, node->stream, nil,
+ nil, false);
}
if (node->tcp_buf)
- ;//free(node->tcp_buf);
+ free(node->tcp_buf);
}
}
@@ -121,13 +108,27 @@ telnet_idle(void)
{
struct telnet_node *node;
short i, error;
+ ip_port port = db->config.telnet_port;
+
+ if (!port)
+ return;
for (i = 0; i < TELNET_SLOTS; i++) {
node = &telnet_nodes[i];
- if (node->state == TELNET_PB_STATE_UNUSED)
- continue;
-
switch (node->state) {
+ case TELNET_PB_STATE_UNUSED:
+ error = _TCPCreate(&node->pb, &node->stream,
+ (Ptr)node->tcp_buf, node->tcp_buf_len, nil, nil, nil, false);
+ if (error)
+ err(1, "TCPCreate[%d] failed: %d", i, error);
+
+ error = _TCPPassiveOpen(&node->listen_pb, node->stream, nil,
+ nil, nil, &port, nil, nil, true);
+ if (error)
+ err(1, "TCPPassiveOpen[%d] on port %d failed: %d", i, port,
+ error);
+ node->state = TELNET_PB_STATE_LISTENING;
+ break;
case TELNET_PB_STATE_LISTENING:
if (node->listen_pb.ioResult > 0)
continue;
@@ -148,11 +149,35 @@ telnet_idle(void)
}
break;
case TELNET_PB_STATE_CONNECTED:
+ if (node->session) {
+ telnet_output(node->session);
+ telnet_input(node->session);
+ }
break;
}
}
}
+void
+telnet_close(struct session *session)
+{
+ struct telnet_node *node = (struct telnet_node *)session->cookie;
+ short error;
+
+ error = _TCPClose(&telnet_exit_pb, node->stream, nil, nil, false);
+ /* TODO: allow some time to fully close? */
+ error = _TCPRelease(&telnet_exit_pb, node->stream, nil, nil, false);
+
+ if (error) {
+ warn("error shutting down telnet session: %d", error);
+ return;
+ }
+
+ session->cookie = NULL;
+ node->session = NULL;
+ node->state = TELNET_PB_STATE_UNUSED;
+}
+
short
telnet_output(struct session *session)
{
@@ -168,12 +193,17 @@ telnet_output(struct session *session)
if (node->tcp_wds[0].length) {
/* previous _TCPSend completed, shift out those bytes */
- BlockMove(session->obuf + node->tcp_wds[0].length,
- session->obuf, session->obuflen - node->tcp_wds[0].length);
session->obuflen -= node->tcp_wds[0].length;
+ if (session->obuflen < 0)
+ warn("bogus obuflen %d", session->obuflen);
+ if (session->obuflen > 0)
+ BlockMove(session->obuf + node->tcp_wds[0].length,
+ session->obuf, session->obuflen);
+ node->tcp_wds[0].length = 0;
+
if (session->obuflen == 0)
- return 0;
+ return node->tcp_wds[0].length;
}
/*
@@ -191,8 +221,8 @@ telnet_output(struct session *session)
if (error)
/* TODO: make this not fatal */
err(1, "TCPSend[%d] failed: %d", node->id, error);
-
- return session->obuflen;
+
+ return 0;
}
short
@@ -211,10 +241,12 @@ telnet_input(struct session *session)
error = _TCPStatus(&node->pb, node->stream, &telnet_status_pb, nil,
nil, false);
- if (error)
- /* TODO: make this not fatal */
- err(1, "TCPRecv[%d] failed: %d", node->id, error);
-
+ if (error ||
+ telnet_status_pb.connectionState != ConnectionStateEstablished) {
+ telnet_close(session);
+ return 0;
+ }
+
if (telnet_status_pb.amtUnreadData == 0)
return 0;
--- telnet.h Mon Nov 29 15:26:06 2021
+++ telnet.h Mon Dec 6 17:33:07 2021
@@ -14,5 +14,11 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#include "session.h"
+
void telnet_init(void);
-void telnet_idle(void);
+void telnet_idle(void);
+
+void telnet_close(struct session *session);
+short telnet_output(struct session *session);
+short telnet_input(struct session *session);