mirror of https://github.com/buserror/mii_emu.git
307 lines
7.2 KiB
C
307 lines
7.2 KiB
C
/*
|
|
* mish_session.c
|
|
*
|
|
* Copyright (C) 2020 Michel Pollet <buserror@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <sys/socket.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h> // for isatty etc
|
|
#include <ctype.h> // for isdigit
|
|
#ifdef __MACH__
|
|
#include <mach/clock.h>
|
|
#include <mach/mach.h>
|
|
#include <util.h> // for openpty
|
|
#endif
|
|
|
|
#ifdef __linux__
|
|
#include <pty.h>
|
|
#endif
|
|
|
|
#include "mish_priv.h"
|
|
#include "mish.h"
|
|
|
|
/*
|
|
* get a localtime equivalent, with milliseconds added
|
|
*/
|
|
uint64_t
|
|
_mish_stamp_ms()
|
|
{
|
|
struct timespec tim;
|
|
#ifdef __MACH__
|
|
clock_serv_t cclock;
|
|
mach_timespec_t mts;
|
|
host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
|
|
clock_get_time(cclock, &mts);
|
|
mach_port_deallocate(mach_task_self(), cclock);
|
|
tim.tv_sec = mts.tv_sec;
|
|
tim.tv_nsec = mts.tv_nsec;
|
|
#else
|
|
clock_gettime(CLOCK_REALTIME, &tim);
|
|
#endif
|
|
|
|
uint64_t res = (tim.tv_sec * 1000) + (tim.tv_nsec / 1000000);
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* need to keep this around for atexit()
|
|
*/
|
|
static mish_p _mish = NULL;
|
|
|
|
static void
|
|
_mish_atexit()
|
|
{
|
|
if (_mish)
|
|
mish_terminate(_mish);
|
|
}
|
|
|
|
mish_p
|
|
mish_prepare(
|
|
unsigned long caps )
|
|
{
|
|
/* In production, of if you want to make sure mish is disabled without
|
|
* re-linking, set this env variable.
|
|
*/
|
|
if (getenv("MISH_OFF")) {
|
|
if (atoi(getenv("MISH_OFF"))) {
|
|
unsetenv("MISH_TELNET_PORT");
|
|
printf("mish: Disabled my MISH_OFF\n");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
mish_p m = (mish_p)calloc(1, sizeof(*m));
|
|
|
|
FD_ZERO(&m->select.read);
|
|
FD_ZERO(&m->select.write);
|
|
TAILQ_INIT(&m->backlog.log);
|
|
TAILQ_INIT(&m->clients);
|
|
m->flags = caps;
|
|
int tty = 0;
|
|
if (getenv("MISH_TTY")) {
|
|
tty = atoi(getenv("MISH_TTY"));
|
|
} else
|
|
tty = (isatty(0) && isatty(1) && isatty(2)) ||
|
|
(caps & MISH_CAP_FORCE_PTY);
|
|
int io[2]; // stdout pipe
|
|
int ie[2]; // stderr pipe
|
|
#if !defined(__wasm__)
|
|
{
|
|
char pty[80];
|
|
if (openpty(&io[0], &io[1], pty, NULL, NULL) == -1) {
|
|
perror("openpty");
|
|
goto error;
|
|
}
|
|
if (!(caps & MISH_CAP_NO_STDERR)) {
|
|
if (openpty(&ie[0], &ie[1], pty, NULL, NULL) == -1) {
|
|
perror("openpty");
|
|
goto error;
|
|
}
|
|
}
|
|
if (tty)
|
|
m->flags |= MISH_CONSOLE_TTY;
|
|
if (tcgetattr(0, &m->orig_termios))
|
|
;//perror("tcgetattr");
|
|
struct termios raw = m->orig_termios;
|
|
raw.c_iflag &= ~(ICRNL | IXON);
|
|
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN); // ISIG
|
|
if (tcsetattr(0, TCSAFLUSH, &raw))
|
|
;//perror("tcsetattr");
|
|
}
|
|
#endif
|
|
if (!(caps & MISH_CAP_NO_TELNET)) {
|
|
uint16_t port = 0; // suggested telnet port
|
|
if (getenv("MISH_TELNET_PORT"))
|
|
port = atoi(getenv("MISH_TELNET_PORT"));
|
|
|
|
if (mish_telnet_prepare(m, port) == 0) {
|
|
char port[8];
|
|
snprintf(port, sizeof(port), "%d", m->telnet.port);
|
|
setenv("MISH_TELNET_PORT", port, 1);
|
|
} else
|
|
unsetenv("MISH_TELNET_PORT");
|
|
}
|
|
|
|
// backup the existing descriptors, to make a 'client', we replace
|
|
// the original 1,2 with out own pipe/pty
|
|
m->originals[0] = dup(1);
|
|
m->originals[1] = dup(2);
|
|
// we do another dup(0,1) here, as that client will close() them
|
|
m->console = mish_client_new(m, dup(0), dup(1), tty);
|
|
m->stamp_start = _mish_stamp_ms();
|
|
|
|
_mish_input_init(m, &m->origin[0], io[0]);
|
|
if (dup2(io[1], 1) == -1) {
|
|
perror("dup2");
|
|
goto error;
|
|
}
|
|
if (!(caps & MISH_CAP_NO_STDERR)) {
|
|
_mish_input_init(m, &m->origin[1], ie[0]);
|
|
if (dup2(ie[1], 2) == -1) {
|
|
perror("dup2");
|
|
goto error;
|
|
}
|
|
}
|
|
atexit(_mish_atexit);
|
|
// m->main = pthread_self();
|
|
// TODO: Make an epoll polling thread for linux
|
|
pthread_create(&m->capture, NULL, _mish_capture_select, m);
|
|
|
|
_mish = m;
|
|
return m;
|
|
error:
|
|
if (io[0]) {
|
|
close(io[0]);
|
|
close(io[1]);
|
|
}
|
|
if (ie[0]) {
|
|
close(ie[0]);
|
|
close(ie[1]);
|
|
}
|
|
free(m);
|
|
return NULL;
|
|
}
|
|
|
|
unsigned long
|
|
mish_get_flags(
|
|
struct mish_t *m)
|
|
{
|
|
return m ? m->flags : 0;
|
|
}
|
|
|
|
/*
|
|
* This can be called at any time; if the thread is running, tell it to
|
|
* close everything and exit... then wait for a little bit for it to
|
|
* terminate, then restore the terminal.
|
|
*/
|
|
void
|
|
mish_terminate(
|
|
mish_p m)
|
|
{
|
|
if (!_mish)
|
|
return;
|
|
// restore normal IOs
|
|
dup2(m->originals[0], 1);
|
|
dup2(m->originals[1], 2);
|
|
// the thread does this, but we also do it in case this function is
|
|
// called from another exit(x)
|
|
#if !defined(__wasm__)
|
|
if ((m->flags & MISH_CONSOLE_TTY) &&
|
|
tcsetattr(0, TCSAFLUSH, &m->orig_termios))
|
|
perror("mish_terminate tcsetattr");
|
|
#endif
|
|
close(m->originals[0]); close(m->originals[1]);
|
|
if (m->capture) {
|
|
m->flags |= MISH_QUIT;
|
|
// this will wake the select() call from sleep
|
|
if (write(1, "\n", 1))
|
|
;
|
|
|
|
time_t start = time(NULL);
|
|
time_t now;
|
|
while (((now = time(NULL)) - start < 2) && m->capture)
|
|
usleep(1000);
|
|
}
|
|
printf("\033[4l\033[;r\033[999;1H"); fflush(stdout);
|
|
printf("%s done\n", __func__);
|
|
free(m);
|
|
_mish = NULL;
|
|
}
|
|
|
|
static void
|
|
_mish_cmd_quit(
|
|
void * param,
|
|
int argc,
|
|
const char * argv[])
|
|
{
|
|
printf(MISH_COLOR_RED
|
|
"mish: Quitting."
|
|
MISH_COLOR_RESET "\n");
|
|
|
|
mish_p m = ((mish_client_p)param)->mish;
|
|
m->flags |= MISH_QUIT;
|
|
}
|
|
|
|
MISH_CMD_NAMES(quit, "q", "quit");
|
|
MISH_CMD_HELP(quit,
|
|
"exit running program",
|
|
"Close all clients and exit(0)");
|
|
MISH_CMD_REGISTER(quit, _mish_cmd_quit);
|
|
|
|
#define VT_COL(_c) "\033[" #_c "G"
|
|
|
|
static void
|
|
_mish_cmd_mish(
|
|
void * param,
|
|
int argc,
|
|
const char * argv[])
|
|
{
|
|
printf(MISH_COLOR_RED
|
|
"mish: mish command."
|
|
MISH_COLOR_RESET "\n");
|
|
|
|
mish_p m = ((mish_client_p)param)->mish;
|
|
printf("Backlog: %6d lines (%5dKB)" VT_COL(40) "Telnet Port: %5d\n",
|
|
m->backlog.size,
|
|
(int)m->backlog.alloc / 1024,
|
|
m->telnet.port);
|
|
#if 0
|
|
printf(" read: ");
|
|
for (int i = 0; i < m->select.max; i++)
|
|
if (FD_ISSET(i, &m->select.read)) printf("%d ", i);
|
|
printf(VT_COL(40) " write: ");
|
|
for (int i = 0; i < m->select.max; i++)
|
|
if (FD_ISSET(i, &m->select.write)) printf("%d ", i);
|
|
printf("\n");
|
|
#endif
|
|
mish_client_p c;
|
|
TAILQ_FOREACH(c, &m->clients, self) {
|
|
printf(" Client: r: %d w: %d %s %s\n",
|
|
c->input.fd, c->output.fd,
|
|
c->input.is_telnet ? "telnet session" :
|
|
c == m->console ? "console" : "*unknown*",
|
|
c == m->console ?
|
|
m->flags & MISH_CONSOLE_TTY ? "(tty)": "(dumb)"
|
|
: "");
|
|
printf(" max sizes: vector: %d input: %d\n",
|
|
c->output.size, c->input.line ? c->input.line->size : 0);
|
|
}
|
|
if (argv[1] && !strcmp(argv[1], "clear")) {
|
|
printf("Clearing backlog\n");
|
|
m->flags |= MISH_CLEAR_BACKLOG;
|
|
}
|
|
if (argv[1] && !strcmp(argv[1], "backlog")) {
|
|
if (argv[2]) {
|
|
if (!strcmp(argv[2], "clear")) {
|
|
m->flags |= MISH_CLEAR_BACKLOG;
|
|
} else if (!strcmp(argv[2], "max") && argv[3] && isdigit(argv[3][0])) {
|
|
m->backlog.max_lines = atoi(argv[3]);
|
|
printf("Backlog max lines set to %d\n", m->backlog.max_lines);
|
|
} else if (isdigit(argv[2][0])) {
|
|
m->backlog.max_lines = atoi(argv[2]);
|
|
printf("Backlog max lines set to %d\n", m->backlog.max_lines);
|
|
} else
|
|
fprintf(stderr, "Unknown backlog command '%s'\n", argv[2]);
|
|
} else {
|
|
printf("Backlog: %6d/%6d lines (%5dKB)\n",
|
|
m->backlog.size, m->backlog.max_lines,
|
|
(int)m->backlog.alloc / 1024);
|
|
}
|
|
}
|
|
}
|
|
|
|
MISH_CMD_NAMES(mish, "mish");
|
|
MISH_CMD_HELP(mish,
|
|
"[cmd...] Displays mish status.",
|
|
"backlog [clear] [max <n>] - show backlog status\n"
|
|
" also set the maximum lines in the backlog\n"
|
|
" (0 = unlimited)\n"
|
|
"Show status and a few bits of internals.");
|
|
MISH_CMD_REGISTER(mish, _mish_cmd_mish);
|
|
|