#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <thread.h>
#include <ctype.h>
#include <sys/termio.h>
#include <libintl.h>
#include <syslog.h>
#include "vntsd.h"
#include "chars.h"
char vntsd_eol[] = { CR, LF, 0};
typedef int (*e_func_t)(vntsd_client_t *clientp);
typedef struct {
char e_char;
char *e_help;
e_func_t e_func;
} esctable_t;
static int
genbrk(vntsd_client_t *clientp)
{
vntsd_cons_t *consp;
assert(clientp);
assert(clientp->cons);
consp = clientp->cons;
D1(stderr, "t@%d genbrk fd=%d sockfd %d\n", thr_self(),
consp->vcc_fd, clientp->sockfd);
assert(consp->clientpq != NULL);
if (consp->clientpq->handle != clientp) {
return (vntsd_write_line(clientp,
gettext(VNTSD_NO_WRITE_ACCESS_MSG)));
}
if (ioctl(consp->vcc_fd, TCSBRK, NULL)) {
return (VNTSD_ERR_VCC_IOCTL);
}
return (VNTSD_STATUS_CONTINUE);
}
static int
genaltbrk(vntsd_client_t *clientp)
{
vntsd_cons_t *consp;
char brkseq[2] = { '~', CNTRL('B')};
assert(clientp);
assert(clientp->cons);
consp = clientp->cons;
D1(stderr, "t@%d genaltbrk fd=%d sockfd %d\n", thr_self(),
consp->vcc_fd, clientp->sockfd);
assert(consp->clientpq != NULL);
if (consp->clientpq->handle != clientp) {
return (vntsd_write_line(clientp,
gettext(VNTSD_NO_WRITE_ACCESS_MSG)));
}
if ((vntsd_write_fd(clientp->cons->vcc_fd, brkseq, sizeof (brkseq))) ==
VNTSD_SUCCESS)
return (VNTSD_STATUS_CONTINUE);
else
return (VNTSD_STATUS_VCC_IO_ERR);
}
static int
console_forward(vntsd_client_t *clientp)
{
if (clientp->cons->group->num_cons > 1)
return (VNTSD_STATUS_MOV_CONS_FORWARD);
return (VNTSD_STATUS_CONTINUE);
}
static int
console_backward(vntsd_client_t *clientp)
{
if (clientp->cons->group->num_cons > 1)
return (VNTSD_STATUS_MOV_CONS_BACKWARD);
return (VNTSD_STATUS_CONTINUE);
}
static int
acquire_write(vntsd_client_t *clientp)
{
int rv;
int yes_no = 1;
vntsd_cons_t *consp;
assert(clientp);
consp = clientp->cons;
assert(consp);
if (consp->clientpq->handle == clientp) {
if ((rv = vntsd_write_line(clientp,
gettext("You have write permission"))) !=
VNTSD_SUCCESS) {
return (rv);
}
return (VNTSD_STATUS_CONTINUE);
}
if ((rv = vntsd_write_client(clientp, vntsd_eol, VNTSD_EOL_LEN))
!= VNTSD_SUCCESS) {
return (rv);
}
if ((rv = vntsd_write_lines(clientp,
gettext("Warning: another user currently "
"has write permission\nto this console and forcibly removing "
"him/her will terminate\nany current write action and all work "
"will be lost."))) != VNTSD_SUCCESS) {
return (rv);
}
if ((rv = vntsd_write_client(clientp, vntsd_eol,
VNTSD_EOL_LEN)) != VNTSD_SUCCESS) {
return (rv);
}
if ((rv = vntsd_get_yes_no(clientp,
gettext("Would you like to continue?"),
&yes_no)) != VNTSD_SUCCESS) {
return (rv);
}
if (yes_no == B_FALSE) {
return (VNTSD_STATUS_CONTINUE);
}
return (VNTSD_STATUS_ACQUIRE_WRITER);
}
static int
client_exit(vntsd_client_t *arg __unused)
{
return (VNTSD_STATUS_RESELECT_CONS);
}
static int daemon_cmd_help(vntsd_client_t *clientp);
static esctable_t etable[] = {
{'#', "Send break", genbrk},
{CNTRL('B'), "Send alternate break", genaltbrk},
{'.', "Exit from this console", client_exit},
{'w', "Force write access", acquire_write},
{'n', "Console next", (e_func_t)console_forward},
{'p', "Console previous", (e_func_t)console_backward},
{'?', "Help", daemon_cmd_help},
{0, 0, 0}
};
void
vntsd_init_esctable_msgs(void)
{
esctable_t *p;
for (p = etable; p->e_char != '\0'; p++) {
p->e_help = gettext(p->e_help);
}
}
static int
daemon_cmd_help(vntsd_client_t *clientp)
{
esctable_t *p;
int rv;
char buf[VNTSD_LINE_LEN];
if ((rv = vntsd_write_client(clientp, vntsd_eol,
VNTSD_EOL_LEN)) != VNTSD_SUCCESS) {
return (rv);
}
if ((rv = vntsd_write_line(clientp, gettext("VNTSD commands"))) !=
VNTSD_SUCCESS) {
return (rv);
}
for (p = etable; p->e_char; p++) {
if (p->e_char == CNTRL('B')) {
(void) snprintf(buf, sizeof (buf), "~^B --%s",
p->e_help);
} else {
(void) snprintf(buf, sizeof (buf),
"~%c --%s", p->e_char, p->e_help);
}
if ((rv = vntsd_write_line(clientp, buf)) != VNTSD_SUCCESS) {
return (rv);
}
}
return (VNTSD_STATUS_CONTINUE);
}
static int
exit_daemon_cmd(vntsd_client_t *clientp, int rv)
{
(void) mutex_lock(&clientp->lock);
clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD;
(void) mutex_unlock(&clientp->lock);
return (rv);
}
int
vntsd_process_daemon_cmd(vntsd_client_t *clientp, char c)
{
esctable_t *p;
int rv;
char prev_char;
prev_char = clientp->prev_char;
if (c != VNTSD_DAEMON_CMD || (prev_char != 0 && prev_char != CR)) {
return (VNTSD_SUCCESS);
}
if (clientp->status & VNTSD_CLIENT_DISABLE_DAEMON_CMD) {
return (VNTSD_STATUS_CONTINUE);
}
(void) mutex_lock(&clientp->lock);
clientp->status |= VNTSD_CLIENT_DISABLE_DAEMON_CMD;
(void) mutex_unlock(&clientp->lock);
D3(stderr, "t@%d process_daemon_cmd %d %d \n", thr_self(),
clientp->cons->vcc_fd, clientp->sockfd);
if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) {
return (exit_daemon_cmd(clientp, rv));
}
if (c == VNTSD_DAEMON_CMD) {
(void) mutex_lock(&clientp->lock);
clientp->status &= ~VNTSD_CLIENT_DISABLE_DAEMON_CMD;
(void) mutex_unlock(&clientp->lock);
return (VNTSD_SUCCESS);
}
for (p = etable; p->e_char; p++) {
if (p->e_char == c) {
assert(p->e_func);
rv = (*p->e_func)(clientp);
return (exit_daemon_cmd(clientp, rv));
}
}
p--;
assert(p->e_char == '?');
rv = (*p->e_func)(clientp);
return (exit_daemon_cmd(clientp, rv));
}
int
vntsd_set_telnet_options(int fd)
{
uint8_t buf[] = {IAC, DONT, LINEMODE, IAC, WILL, SUPRESS, IAC, WILL,
TEL_ECHO, IAC, DONT, TERM_TYPE, IAC, DONT, TERM_SP,
IAC, DONT, STATUS, IAC, DONT, FC, IAC, DONT, TM, IAC, DONT, ENV,
IAC, DONT, WIN_SIZE};
return (vntsd_write_fd(fd, (char *)buf, 30));
}
int
vntsd_telnet_cmd(vntsd_client_t *clientp, char c)
{
uint8_t buf[4];
char cmd;
int rv = VNTSD_STATUS_CONTINUE;
bzero(buf, 4);
if ((uint8_t)c != IAC) {
return (VNTSD_SUCCESS);
}
if ((rv = vntsd_read_char(clientp, &cmd)) != VNTSD_SUCCESS) {
return (rv);
}
if ((uint8_t)cmd == WILL || (uint8_t)cmd == WONT ||
(uint8_t)cmd == DO || (uint8_t)cmd == DONT) {
if ((rv = vntsd_read_char(clientp, &c)) != VNTSD_SUCCESS) {
return (rv);
}
}
switch ((uint8_t)cmd) {
case WILL:
switch ((uint8_t)c) {
case TEL_ECHO:
case SUPRESS:
case LINEMODE:
break;
default:
syslog(LOG_ERR, "not support telnet WILL %x\n", c);
break;
}
break;
case WONT:
switch ((uint8_t)c) {
case TEL_ECHO:
case SUPRESS:
case LINEMODE:
default:
syslog(LOG_ERR, "not support telnet WONT %x\n", c);
break;
}
break;
case DO:
case DONT:
buf[0] = IAC;
buf[1] = WILL;
buf[2] = c;
rv = vntsd_write_client(clientp, (char *)buf, 3);
break;
case BRK:
rv = genbrk(clientp);
break;
case IP:
break;
case AYT: {
static char aytresp[] = "vntsd here";
rv = vntsd_write_client(clientp, aytresp,
sizeof (aytresp) - 1);
break;
}
case HT:
case NOP:
return (VNTSD_STATUS_CONTINUE);
default:
syslog(LOG_ERR, "not support telnet ctrl %2.2x\n", 0xff & cmd);
break;
}
if (rv == VNTSD_SUCCESS) {
return (VNTSD_STATUS_CONTINUE);
} else {
return (rv);
}
}
int
vntsd_ctrl_cmd(vntsd_client_t *clientp, char c)
{
int cmd;
D3(stderr, "t@%d vntsd_ctrl_cmd%d %d\n", thr_self(),
clientp->cons->vcc_fd, clientp->sockfd);
if ((c != START) && (c != STOP)) {
return (VNTSD_SUCCESS);
}
if (c == START) {
D3(stderr, "t@%d client restart\n", thr_self());
cmd = 1;
if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) {
return (VNTSD_STATUS_VCC_IO_ERR);
}
}
if (c == STOP) {
D3(stderr, "t@%d client suspend\n", thr_self());
cmd = 0;
if (ioctl(clientp->cons->vcc_fd, TCXONC, &cmd)) {
return (VNTSD_STATUS_VCC_IO_ERR);
}
}
return (VNTSD_STATUS_CONTINUE);
}