mirror of
https://github.com/buserror/mii_emu.git
synced 2024-11-24 13:33:04 +00:00
libmish: Updated from upstream
Thread safety stuff etc Signed-off-by: Michel Pollet <buserror@gmail.com>
This commit is contained in:
parent
ea04883db3
commit
589a73f98c
@ -9,9 +9,33 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
#include "mish_priv.h"
|
#include "mish_priv.h"
|
||||||
#include "mish.h"
|
#include "mish.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We now use a thread to run the commands; this solve the problem of
|
||||||
|
* command generating a lot of output, deadlocking the select() thread,
|
||||||
|
* as the command write() would fill up the pipe buffer and block.
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
_mish_cmd_runner_thread(
|
||||||
|
void *param)
|
||||||
|
{
|
||||||
|
mish_p m = param;
|
||||||
|
|
||||||
|
printf("%s\n", __func__);
|
||||||
|
while (!(m->flags & MISH_QUIT)) {
|
||||||
|
sem_wait(&m->runner_block);
|
||||||
|
_mish_cmd_flush(0);
|
||||||
|
};
|
||||||
|
printf("Exiting %s\n", __func__);
|
||||||
|
m->cmd_runner = 0;
|
||||||
|
sem_destroy(&m->runner_block);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a select() based capture thread, it's not /ideal/ in terms of
|
* This is a select() based capture thread, it's not /ideal/ in terms of
|
||||||
* performances, but it's portable and should work on OSX/BSD Linux etc.
|
* performances, but it's portable and should work on OSX/BSD Linux etc.
|
||||||
@ -68,6 +92,12 @@ _mish_capture_select(
|
|||||||
_mish_input_read(m, &r, &c->input);
|
_mish_input_read(m, &r, &c->input);
|
||||||
if (c->input.fd == -1 || (c->flags & MISH_CLIENT_DELETE))
|
if (c->input.fd == -1 || (c->flags & MISH_CLIENT_DELETE))
|
||||||
mish_client_delete(m, c);
|
mish_client_delete(m, c);
|
||||||
|
if (c->flags & MISH_CLIENT_HAS_CMD) {
|
||||||
|
// printf("Waking up cmd_runner\n");
|
||||||
|
c->flags &= ~MISH_CLIENT_HAS_CMD;
|
||||||
|
// wake up the command runner
|
||||||
|
sem_post(&m->runner_block);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int max_lines = m->backlog.max_lines;
|
unsigned int max_lines = m->backlog.max_lines;
|
||||||
@ -112,8 +142,9 @@ _mish_capture_select(
|
|||||||
mish_client_p c;
|
mish_client_p c;
|
||||||
while ((c = TAILQ_FIRST(&m->clients)) != NULL)
|
while ((c = TAILQ_FIRST(&m->clients)) != NULL)
|
||||||
mish_client_delete(m, c);
|
mish_client_delete(m, c);
|
||||||
m->flags &= ~MISH_QUIT;
|
// m->flags &= ~MISH_QUIT;
|
||||||
m->capture = 0; // mark the thread done
|
m->capture = 0; // mark the thread done
|
||||||
|
// printf("Exiting %s\n", __func__);
|
||||||
exit(0); // this calls mish_terminate, on main thread
|
exit(0); // this calls mish_terminate, on main thread
|
||||||
// return NULL;
|
// return NULL;
|
||||||
}
|
}
|
||||||
|
@ -354,7 +354,7 @@ _mish_cmd_history(
|
|||||||
MISH_CMD_NAMES(history, "history");
|
MISH_CMD_NAMES(history, "history");
|
||||||
MISH_CMD_HELP(history,
|
MISH_CMD_HELP(history,
|
||||||
"Display the history of commands.");
|
"Display the history of commands.");
|
||||||
MISH_CMD_REGISTER(history, _mish_cmd_history);
|
MISH_CMD_REGISTER_KIND(history, _mish_cmd_history, 0, MISH_CLIENT_CMD_KIND);
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -380,5 +380,5 @@ _mish_cmd_disconnect(
|
|||||||
MISH_CMD_NAMES(disconnect, "dis", "disconnect", "logout");
|
MISH_CMD_NAMES(disconnect, "dis", "disconnect", "logout");
|
||||||
MISH_CMD_HELP(disconnect,
|
MISH_CMD_HELP(disconnect,
|
||||||
"Disconnect this telnet session. If appropriate");
|
"Disconnect this telnet session. If appropriate");
|
||||||
MISH_CMD_REGISTER(disconnect, _mish_cmd_disconnect);
|
MISH_CMD_REGISTER_KIND(disconnect, _mish_cmd_disconnect, 0, MISH_CLIENT_CMD_KIND);
|
||||||
|
|
||||||
|
@ -195,7 +195,10 @@ kb_end:
|
|||||||
fprintf(stdout, "%s", c->cmd->line + c->cmd->done+1);
|
fprintf(stdout, "%s", c->cmd->line + c->cmd->done+1);
|
||||||
fprintf(stdout, "'\n");
|
fprintf(stdout, "'\n");
|
||||||
}
|
}
|
||||||
mish_cmd_call(c->cmd->line, c);
|
// if we have a non-safe command, we need to signal the
|
||||||
|
// cmd execution thread, so mark the client as having a command
|
||||||
|
if (mish_cmd_call(c->cmd->line, c) == 1)
|
||||||
|
c->flags |= MISH_CLIENT_HAS_CMD;
|
||||||
c->cmd = NULL; // new one
|
c->cmd = NULL; // new one
|
||||||
{ // reuse the last empty one
|
{ // reuse the last empty one
|
||||||
mish_line_p last = TAILQ_LAST(&in->backlog, mish_line_queue_t);
|
mish_line_p last = TAILQ_LAST(&in->backlog, mish_line_queue_t);
|
||||||
|
@ -46,7 +46,7 @@ DECLARE_FIFO(mish_cmd_call_t, mish_call_queue, 4);
|
|||||||
DEFINE_FIFO(mish_cmd_call_t, mish_call_queue);
|
DEFINE_FIFO(mish_cmd_call_t, mish_call_queue);
|
||||||
|
|
||||||
static TAILQ_HEAD(,mish_cmd_t) _cmd_list = TAILQ_HEAD_INITIALIZER(_cmd_list);
|
static TAILQ_HEAD(,mish_cmd_t) _cmd_list = TAILQ_HEAD_INITIALIZER(_cmd_list);
|
||||||
static mish_call_queue_t _cmd_fifo = {0};
|
static mish_call_queue_t _cmd_fifo[2] = {0};
|
||||||
|
|
||||||
void __attribute__((weak))
|
void __attribute__((weak))
|
||||||
mish_register_cmd_kind(
|
mish_register_cmd_kind(
|
||||||
@ -146,7 +146,7 @@ mish_argv_make(
|
|||||||
_mish_argv_t * r = calloc(1, sizeof(*r));
|
_mish_argv_t * r = calloc(1, sizeof(*r));
|
||||||
r->line = strdup(line);
|
r->line = strdup(line);
|
||||||
char *dup = r->line;
|
char *dup = r->line;
|
||||||
char quote;
|
char quote = 0;
|
||||||
enum { s_newarg = 0, s_startarg, s_copyquote, s_skip, s_copy };
|
enum { s_newarg = 0, s_startarg, s_copyquote, s_skip, s_copy };
|
||||||
int state = s_newarg;
|
int state = s_newarg;
|
||||||
do {
|
do {
|
||||||
@ -232,39 +232,52 @@ mish_cmd_call(
|
|||||||
int ac = 0;
|
int ac = 0;
|
||||||
char ** av = mish_argv_make(cmd_line, &ac);
|
char ** av = mish_argv_make(cmd_line, &ac);
|
||||||
|
|
||||||
if (cmd->flags.safe) {
|
mish_call_queue_t *fifo = &_cmd_fifo[cmd->flags.safe];
|
||||||
if (!mish_call_queue_isfull(&_cmd_fifo)) {
|
|
||||||
|
// these are special commands, their parameter is the client
|
||||||
|
if (cmd->kind == MISH_CLIENT_CMD_KIND) {
|
||||||
|
cmd->cmd_cb(c, ac, (const char**)av);
|
||||||
|
mish_argv_free(av);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// all other commands are queued
|
||||||
|
if (!mish_call_queue_isfull(fifo)) {
|
||||||
mish_cmd_call_t fe = {
|
mish_cmd_call_t fe = {
|
||||||
.cmd = cmd,
|
.cmd = cmd,
|
||||||
.argv = av,
|
.argv = av,
|
||||||
.argc = ac,
|
.argc = ac,
|
||||||
};
|
};
|
||||||
mish_call_queue_write(&_cmd_fifo, fe);
|
mish_call_queue_write(fifo, fe);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"mish: cmd FIFO full, make sure to call mish_cmd_poll()!\n");
|
"mish: cmd FIFO%d full, make sure to call mish_cmd_poll()!\n",
|
||||||
|
cmd->flags.safe);
|
||||||
}
|
}
|
||||||
} else {
|
return cmd->flags.safe == 0; // we got a command to run?
|
||||||
cmd->cmd_cb(cmd->param_cb ? cmd->param_cb : c, ac, (const char**)av);
|
|
||||||
mish_argv_free(av);
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
int
|
||||||
|
_mish_cmd_flush(
|
||||||
|
unsigned int queue)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
mish_call_queue_t *fifo = &_cmd_fifo[!!queue];
|
||||||
|
while (!mish_call_queue_isempty(fifo)) {
|
||||||
|
mish_cmd_call_t c = mish_call_queue_read(fifo);
|
||||||
|
c.cmd->cmd_cb(
|
||||||
|
c.cmd->param_cb,
|
||||||
|
c.argc, (const char**)c.argv);
|
||||||
|
mish_argv_free(c.argv);
|
||||||
|
res++;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
mish_cmd_poll()
|
mish_cmd_poll()
|
||||||
{
|
{
|
||||||
int res = 0;
|
return _mish_cmd_flush(1);
|
||||||
|
|
||||||
while (!mish_call_queue_isempty(&_cmd_fifo)) {
|
|
||||||
mish_cmd_call_t c = mish_call_queue_read(&_cmd_fifo);
|
|
||||||
|
|
||||||
c.cmd->cmd_cb(
|
|
||||||
c.cmd->param_cb, c.argc, (const char**)c.argv);
|
|
||||||
mish_argv_free(c.argv);
|
|
||||||
res++;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *_help[] = {
|
static const char *_help[] = {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <semaphore.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include "bsd_queue.h"
|
#include "bsd_queue.h"
|
||||||
#include "mish_priv_vt.h"
|
#include "mish_priv_vt.h"
|
||||||
@ -28,6 +29,9 @@ enum {
|
|||||||
MISH_IN_SPLIT, // store current line, add it to backlog
|
MISH_IN_SPLIT, // store current line, add it to backlog
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MISH_CMD_KIND ('m' << 24 | 'i' << 16 | 's' << 8 | 'h')
|
||||||
|
#define MISH_CLIENT_CMD_KIND ('c' << 24 | 'l' << 16 | 'i' << 8 | 'e')
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This receives stuff from a file descriptor, 'processes' it, and split it
|
* This receives stuff from a file descriptor, 'processes' it, and split it
|
||||||
* into lines to eventually store new lines into the 'backlog'.
|
* into lines to eventually store new lines into the 'backlog'.
|
||||||
@ -55,7 +59,8 @@ enum {
|
|||||||
MISH_CLIENT_UPDATE_PROMPT = (1 << 3),
|
MISH_CLIENT_UPDATE_PROMPT = (1 << 3),
|
||||||
MISH_CLIENT_UPDATE_WINDOW = (1 << 4),
|
MISH_CLIENT_UPDATE_WINDOW = (1 << 4),
|
||||||
MISH_CLIENT_SCROLLING = (1 << 5),
|
MISH_CLIENT_SCROLLING = (1 << 5),
|
||||||
MISH_CLIENT_DELETE = (1 << 6),
|
MISH_CLIENT_HAS_CMD = (1 << 6),
|
||||||
|
MISH_CLIENT_DELETE = (1 << 7),
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct mish_client_t {
|
typedef struct mish_client_t {
|
||||||
@ -150,6 +155,8 @@ typedef struct mish_t {
|
|||||||
mish_client_p console; // client that is also the original terminal.
|
mish_client_p console; // client that is also the original terminal.
|
||||||
|
|
||||||
pthread_t capture; // libmish main thread
|
pthread_t capture; // libmish main thread
|
||||||
|
pthread_t cmd_runner; // command runner thread
|
||||||
|
sem_t runner_block; // semaphore to block the runner thread
|
||||||
pthread_t main; // todo: allow pause/stop/resume?
|
pthread_t main; // todo: allow pause/stop/resume?
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -258,13 +265,20 @@ _mish_input_read(
|
|||||||
mish_p m,
|
mish_p m,
|
||||||
fd_set * fds,
|
fd_set * fds,
|
||||||
mish_input_p in);
|
mish_input_p in);
|
||||||
|
// flush the command FIFO (queue = 0 for non-safe commands)
|
||||||
|
int
|
||||||
|
_mish_cmd_flush(
|
||||||
|
unsigned int queue);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Capture thread function
|
* Thread functions
|
||||||
*/
|
*/
|
||||||
void *
|
void *
|
||||||
_mish_capture_select(
|
_mish_capture_select(
|
||||||
void *param);
|
void *param);
|
||||||
|
void *
|
||||||
|
_mish_cmd_runner_thread(
|
||||||
|
void *param);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal_output_sequences
|
* https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal_output_sequences
|
||||||
|
@ -147,9 +147,12 @@ mish_prepare(
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mish_set_command_parameter(MISH_CMD_KIND, m);
|
||||||
atexit(_mish_atexit);
|
atexit(_mish_atexit);
|
||||||
// m->main = pthread_self();
|
// m->main = pthread_self();
|
||||||
// TODO: Make an epoll polling thread for linux
|
// TODO: Make an epoll polling thread for linux
|
||||||
|
sem_init(&m->runner_block, 0, 0);
|
||||||
|
pthread_create(&m->cmd_runner, NULL, _mish_cmd_runner_thread, m);
|
||||||
pthread_create(&m->capture, NULL, _mish_capture_select, m);
|
pthread_create(&m->capture, NULL, _mish_capture_select, m);
|
||||||
|
|
||||||
_mish = m;
|
_mish = m;
|
||||||
@ -196,19 +199,21 @@ mish_terminate(
|
|||||||
perror("mish_terminate tcsetattr");
|
perror("mish_terminate tcsetattr");
|
||||||
#endif
|
#endif
|
||||||
close(m->originals[0]); close(m->originals[1]);
|
close(m->originals[0]); close(m->originals[1]);
|
||||||
if (m->capture) {
|
pthread_t t1 = m->cmd_runner, t2 = m->capture;
|
||||||
m->flags |= MISH_QUIT;
|
m->flags |= MISH_QUIT;
|
||||||
|
if (t1)
|
||||||
|
sem_post(&m->runner_block);
|
||||||
|
if (t2) {
|
||||||
// this will wake the select() call from sleep
|
// this will wake the select() call from sleep
|
||||||
if (write(1, "\n", 1))
|
if (write(1, "\n", 1))
|
||||||
;
|
;
|
||||||
|
|
||||||
time_t start = time(NULL);
|
time_t start = time(NULL);
|
||||||
time_t now;
|
time_t now;
|
||||||
while (((now = time(NULL)) - start < 2) && m->capture)
|
while (((now = time(NULL)) - start < 2) && m->capture)
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
printf("\033[4l\033[;r\033[999;1H"); fflush(stdout);
|
printf("\033[4l\033[;r\033[999;1H"); fflush(stdout);
|
||||||
printf("%s done\n", __func__);
|
//printf("%s done\n", __func__);
|
||||||
free(m);
|
free(m);
|
||||||
_mish = NULL;
|
_mish = NULL;
|
||||||
}
|
}
|
||||||
@ -223,7 +228,7 @@ _mish_cmd_quit(
|
|||||||
"mish: Quitting."
|
"mish: Quitting."
|
||||||
MISH_COLOR_RESET "\n");
|
MISH_COLOR_RESET "\n");
|
||||||
|
|
||||||
mish_p m = ((mish_client_p)param)->mish;
|
mish_p m = param;
|
||||||
m->flags |= MISH_QUIT;
|
m->flags |= MISH_QUIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +236,7 @@ MISH_CMD_NAMES(quit, "q", "quit");
|
|||||||
MISH_CMD_HELP(quit,
|
MISH_CMD_HELP(quit,
|
||||||
"exit running program",
|
"exit running program",
|
||||||
"Close all clients and exit(0)");
|
"Close all clients and exit(0)");
|
||||||
MISH_CMD_REGISTER(quit, _mish_cmd_quit);
|
MISH_CMD_REGISTER_KIND(quit, _mish_cmd_quit, 0, MISH_CMD_KIND);
|
||||||
|
|
||||||
#define VT_COL(_c) "\033[" #_c "G"
|
#define VT_COL(_c) "\033[" #_c "G"
|
||||||
|
|
||||||
@ -245,7 +250,7 @@ _mish_cmd_mish(
|
|||||||
"mish: mish command."
|
"mish: mish command."
|
||||||
MISH_COLOR_RESET "\n");
|
MISH_COLOR_RESET "\n");
|
||||||
|
|
||||||
mish_p m = ((mish_client_p)param)->mish;
|
mish_p m = param;
|
||||||
printf("Backlog: %6d lines (%5dKB)" VT_COL(40) "Telnet Port: %5d\n",
|
printf("Backlog: %6d lines (%5dKB)" VT_COL(40) "Telnet Port: %5d\n",
|
||||||
m->backlog.size,
|
m->backlog.size,
|
||||||
(int)m->backlog.alloc / 1024,
|
(int)m->backlog.alloc / 1024,
|
||||||
@ -302,5 +307,5 @@ MISH_CMD_HELP(mish,
|
|||||||
" also set the maximum lines in the backlog\n"
|
" also set the maximum lines in the backlog\n"
|
||||||
" (0 = unlimited)\n"
|
" (0 = unlimited)\n"
|
||||||
"Show status and a few bits of internals.");
|
"Show status and a few bits of internals.");
|
||||||
MISH_CMD_REGISTER(mish, _mish_cmd_mish);
|
MISH_CMD_REGISTER_KIND(mish, _mish_cmd_mish, 0, MISH_CMD_KIND);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user