From 13b1d7fad29b1f22cf050b292388647a64b41797 Mon Sep 17 00:00:00 2001 From: lxsang Date: Thu, 6 Aug 2020 23:17:46 +0200 Subject: [PATCH] first working vterm --- configure.ac | 17 +- log.h | 6 +- tunnel.c | 52 +++--- tunnel.h | 1 + vterm/vterm | Bin 47344 -> 68352 bytes vterm/vterm.c | 439 ++++++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 450 insertions(+), 65 deletions(-) diff --git a/configure.ac b/configure.ac index bdac9c3..4611741 100644 --- a/configure.ac +++ b/configure.ac @@ -12,7 +12,22 @@ AC_PROG_CC AC_PROG_LIBTOOL AC_DEFINE([_GNU_SOURCE], [1],[Use GNU source]) -# AC_CANONICAL_HOST is needed to access the 'host_os' variable +# AC_CANONICAL_HOST is needed to access the 'host_os' variable + +has_antd=no +# check for lib antd +AC_CHECK_HEADER([antd/plugin.h],[ + has_antd=yes + # check if the library exists +],[ + AC_MSG_ERROR([Unable to find antd, please install it first]) +]) +AC_CHECK_LIB([antd],[antd_send],[],[ + if test "$has_antd" = "yes"; then + AC_MSG_ERROR([Unable to find antd shared library, please install it first]) + fi +]) + # debug option AC_ARG_ENABLE([debug], diff --git a/log.h b/log.h index 7638e84..f1716f9 100644 --- a/log.h +++ b/log.h @@ -9,11 +9,11 @@ } while(0) #ifdef DEBUG - #define LOG(m, a,...) syslog ((LOG_NOTICE),m "_log@[%s: %d]: " a "\n", __FILE__, \ + #define M_LOG(m, a,...) syslog ((LOG_NOTICE),m "_log@[%s: %d]: " a "\n", __FILE__, \ __LINE__, ##__VA_ARGS__) #else - #define LOG(m, a,...) do{}while(0) + #define M_LOG(m, a,...) do{}while(0) #endif -#define ERROR(m, a,...) syslog ((LOG_ERR),m "_error@[%s: %d]: " a "\n", __FILE__, \ +#define M_ERROR(m, a,...) syslog ((LOG_ERR),m "_error@[%s: %d]: " a "\n", __FILE__, \ __LINE__, ##__VA_ARGS__) #endif \ No newline at end of file diff --git a/tunnel.c b/tunnel.c index 15d888b..d009ff4 100644 --- a/tunnel.c +++ b/tunnel.c @@ -17,12 +17,12 @@ static int msg_check_number(int fd, int number) int value; if(read(fd,&value,sizeof(value)) == -1) { - ERROR(MODULE_NAME, "Unable to read integer value: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to read integer value: %s", strerror(errno)); return -1; } if(number != value) { - ERROR(MODULE_NAME, "Value mismatches: %04X, expected %04X", value, number); + M_ERROR(MODULE_NAME, "Value mismatches: %04X, expected %04X", value, number); return -1; } return 0; @@ -32,17 +32,17 @@ static int msg_read_string(int fd, char* buffer, uint8_t max_length) uint8_t size; if(read(fd,&size,sizeof(size)) == -1) { - ERROR(MODULE_NAME, "Unable to read string size: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to read string size: %s", strerror(errno)); return -1; } if(size > max_length) { - ERROR(MODULE_NAME, "String length exceeds the maximal value of %d", max_length); + M_ERROR(MODULE_NAME, "String length exceeds the maximal value of %d", max_length); return -1; } if(read(fd,buffer,size) == -1) { - ERROR(MODULE_NAME, "Unable to read string to buffer: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to read string to buffer: %s", strerror(errno)); return -1; } return 0; @@ -53,7 +53,7 @@ static uint8_t* msg_read_payload(int fd, int* size) uint8_t* data; if(read(fd,size,sizeof(*size)) == -1) { - ERROR(MODULE_NAME, "Unable to read payload data size: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to read payload data size: %s", strerror(errno)); return NULL; } if(*size <= 0) @@ -64,12 +64,12 @@ static uint8_t* msg_read_payload(int fd, int* size) data = (uint8_t*) malloc(*size); if(data == NULL) { - ERROR(MODULE_NAME, "Unable to allocate memory for payload data: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to allocate memory for payload data: %s", strerror(errno)); return NULL; } if(read(fd,data,*size) == -1) { - ERROR(MODULE_NAME, "Unable to read payload data to buffer: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to read payload data to buffer: %s", strerror(errno)); free(data); return NULL; } @@ -86,15 +86,15 @@ int open_unix_socket(char* path) int fd = socket(AF_UNIX, SOCK_STREAM, 0); if(fd == -1) { - ERROR(MODULE_NAME, "Unable to create Unix domain socket: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to create Unix domain socket: %s", strerror(errno)); return -1; } if(connect(fd, (struct sockaddr*)(&address), sizeof(address)) == -1) { - ERROR(MODULE_NAME, "Unable to connect to socket '%s': %s", address.sun_path, strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to connect to socket '%s': %s", address.sun_path, strerror(errno)); return -1; } - LOG(MODULE_NAME, "Socket %s is created successfully", path); + M_LOG(MODULE_NAME, "Socket %s is created successfully", path); return fd; } @@ -103,32 +103,32 @@ int msg_read(int fd, tunnel_msg_t* msg) { if(msg_check_number(fd, MSG_MAGIC_BEGIN) == -1) { - ERROR(MODULE_NAME, "Unable to check begin magic number"); + M_ERROR(MODULE_NAME, "Unable to check begin magic number"); return -1; } if(read(fd,&msg->header.type,sizeof(msg->header.type)) == -1) { - ERROR(MODULE_NAME, "Unable to read msg type: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to read msg type: %s", strerror(errno)); return -1; } - if(msg->header.type > 0x6) + if(msg->header.type > 0x7) { - ERROR(MODULE_NAME, "Unknown msg type: %d", msg->header.type); + M_ERROR(MODULE_NAME, "Unknown msg type: %d", msg->header.type); return -1; } if(read(fd, &msg->header.channel_id, sizeof(msg->header.channel_id)) == -1) { - ERROR(MODULE_NAME, "Unable to read msg channel id"); + M_ERROR(MODULE_NAME, "Unable to read msg channel id"); return -1; } if(read(fd, &msg->header.client_id, sizeof(msg->header.client_id)) == -1) { - ERROR(MODULE_NAME, "Unable to read msg client id"); + M_ERROR(MODULE_NAME, "Unable to read msg client id"); return -1; } if((msg->data = msg_read_payload(fd, &msg->header.size)) == NULL && msg->header.size != 0) { - ERROR(MODULE_NAME, "Unable to read msg payload data"); + M_ERROR(MODULE_NAME, "Unable to read msg payload data"); return -1; } if(msg_check_number(fd, MSG_MAGIC_END) == -1) @@ -137,7 +137,7 @@ int msg_read(int fd, tunnel_msg_t* msg) { free(msg->data); } - ERROR(MODULE_NAME, "Unable to check end magic number"); + M_ERROR(MODULE_NAME, "Unable to check end magic number"); return -1; } return 0; @@ -149,32 +149,32 @@ int msg_write(int fd, tunnel_msg_t* msg) int number = MSG_MAGIC_BEGIN; if(write(fd,&number, sizeof(number)) == -1) { - ERROR(MODULE_NAME, "Unable to write begin magic number: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to write begin magic number: %s", strerror(errno)); return -1; } // write type if(write(fd,&msg->header.type, sizeof(msg->header.type)) == -1) { - ERROR(MODULE_NAME, "Unable to write msg type: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to write msg type: %s", strerror(errno)); return -1; } // write channel id if(write(fd,&msg->header.channel_id, sizeof(msg->header.channel_id)) == -1) { - ERROR(MODULE_NAME, "Unable to write msg channel id: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to write msg channel id: %s", strerror(errno)); return -1; } //write client id if(write(fd,&msg->header.client_id, sizeof(msg->header.client_id)) == -1) { - ERROR(MODULE_NAME, "Unable to write msg client id: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to write msg client id: %s", strerror(errno)); return -1; } // write payload len if(write(fd,&msg->header.size, sizeof(msg->header.size)) == -1) { - ERROR(MODULE_NAME, "Unable to write msg payload length: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to write msg payload length: %s", strerror(errno)); return -1; } // write payload data @@ -182,14 +182,14 @@ int msg_write(int fd, tunnel_msg_t* msg) { if(write(fd,msg->data, msg->header.size) == -1) { - ERROR(MODULE_NAME, "Unable to write msg payload: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to write msg payload: %s", strerror(errno)); return -1; } } number = MSG_MAGIC_END; if(write(fd,&number, sizeof(number)) == -1) { - ERROR(MODULE_NAME, "Unable to write end magic number: %s", strerror(errno)); + M_ERROR(MODULE_NAME, "Unable to write end magic number: %s", strerror(errno)); return -1; } return 0; diff --git a/tunnel.h b/tunnel.h index e46f4b5..90f04ac 100644 --- a/tunnel.h +++ b/tunnel.h @@ -16,6 +16,7 @@ #define CHANNEL_DATA (uint8_t)0x6 #define CHANNEL_UNSUBSCRIBE (uint8_t)0x3 #define CHANNEL_SUBSCRIBE (uint8_t)0x2 +#define CHANNEL_CTRL (uint8_t)0x7 typedef struct { uint8_t type; diff --git a/vterm/vterm b/vterm/vterm index fd9fdc3c6c44f022559617ea9f04eefd1c2f7033..f66463f32a62633142ca1cb038955628f31a43c1 100755 GIT binary patch literal 68352 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�fwOC7?+IWq>(~^uY z(b9b<#Hp2<-&IMb#~{7dijlBWo1hh?#jAIVZ7fc6)J`sNYU&O8WHj1cz2TpNpKQa( ztWyhQz^8FOD4p8W6nYw_v$S`|>;?E09`vFzjej{^bzih>dU~2YIS_0IOOqv0jR=3# zhOblje04u$u5$qB7C)1HbkJO0Pt(3v15fhT+T;ug`qVhN7x;Ab{jQS3`>T&2Pe-Qu z{upICzP_{LOhJ}(NhklPpeRm>&)*9A9C#dv6ko^spHQNGRXN=2K?I@@DA9Y&Oh{goen=2c(T)8-}(&n&A_Mg zTO05NjxvqE3&bAjCp>Acl^E`)*JhCa`3!iPw~+sjD&-NS;0GD#e}nRT^wUz?czA}A z)35sbY$fLx3cp$PQ~WH8IB2PmU%J|HYkzTB8W&Yi$dO(qq&$1>2H+d+9_VcS4F z7RP?wnl=sl-S8#V81@y0g!8VGmVb~Vj7w+uo+tS(-ZpT)}Sh#f%yJ7o!`+Fkz z#%#^P+WBXsC&C8cXm_}EV4!t7HjBmwwqqADw(*8}|Ewj4-L2j6BvSPfOqJ~hptHA+wlji2G)a4`Pi9%QCq%97fA>bW)S2ZE!>g0;q4m&fB&PyQQ&_q6WiX4 zO~**%12XMm2(F1>FMdrFdys4Jes&FZBlkxJ;@d4K+KJj;-QBKG!KIDZ!rZ9|@-A#q z)@s_fN1;Ndcn3k0Y{f2FY+6prgi$sS>1ibf1@-sDwHncKH2`Zm`;aAo_FdP+BioRr z5dzeGBED-PUD)kQBScp_q;XElkb{HdU>K^fuN=Z8J6d0>!NAmu!3Ma;Xwwm?VUj-Da}~>zk{NgKc)(pW66V`J2~Z5MO7?l z8K(3Uob;&2{QP{%X#i!`13!e4bkJXcto$vwU(ms6rOcXFo*ys*KYEkI&(FJ@2AEK# zcK!>1{}A7JWc&HKm{ZozkH2>NTftAiR>1sEEBiU6c@iB|NAffC^W#J}aKys={G81x z-|s;p4w7L#E=PvGVafdbJkF__R?)ln;$S*XuK||6iOKn$WGD_!d3t2GUsB1nR`MzH z^YcBYhZG^}x7YuRC@21W(NLy75`lR%{Ikg=cga{x8sQP zl}jZ?yR%GVrfdhF@*#fOcKQsV>Wz$o*~&*_DVpY7u`zFR{4 zcOu3!ALk<(`0u5G6~|6n0ND8{Z|kChuj;EJ!LPi@l;U6^PL5l?e^N2Wo}`r5s&C8m bIa{G!KMl7w62bW41CpP9c+Ps*vfBRwxOzzc diff --git a/vterm/vterm.c b/vterm/vterm.c index 11cb0ec..bd0cef6 100644 --- a/vterm/vterm.c +++ b/vterm/vterm.c @@ -5,26 +5,308 @@ #include #include #include +#include +#include +#include +#include +#include + +#include +#include + #include "tunnel.h" #define MODULE_NAME "vterm" +typedef struct{ + int fdm; + pid_t pid; + int cid; +} vterm_proc_t; + +static bst_node_t* processes = NULL; + static volatile int running = 1; -void int_handler(int dummy) { +static void int_handler(int dummy) { (void) dummy; running = 0; } +static vterm_proc_t* terminal_new(void) +{ + int fdm, fds, rc; + pid_t pid; + vterm_proc_t* proc = NULL; + // Check arguments + fdm = posix_openpt(O_RDWR); + if (fdm < 0) + { + M_LOG(MODULE_NAME, "Error on posix_openpt(): %s\n", strerror(errno)); + return NULL; + } + + rc = grantpt(fdm); + if (rc != 0) + { + M_LOG(MODULE_NAME, "Error on grantpt(): %s\n", strerror(errno)); + return NULL; + } + rc = unlockpt(fdm); + if (rc != 0) + { + M_LOG(MODULE_NAME, "Error on unlockpt(): %s\n", strerror(errno)); + return NULL; + } + + // Open the slave side ot the PTY + fds = open(ptsname(fdm), O_RDWR); + + // Create the child process + pid = fork(); + if (pid) + { + // parent + proc = (vterm_proc_t*)malloc(sizeof(vterm_proc_t)); + proc->fdm = fdm; + proc->pid = pid; + return proc; + } + else + { + //struct termios slave_orig_term_settings; // Saved terminal settings + //struct termios new_term_settings; // Current terminal settings + + // CHILD + + // Close the master side of the PTY + close(fdm); + + // Save the defaults parameters of the slave side of the PTY + //rc = tcgetattr(fds, &slave_orig_term_settings); + + // Set RAW mode on slave side of PTY + //new_term_settings = slave_orig_term_settings; + //cfmakeraw (&new_term_settings); + //tcsetattr (fds, TCSANOW, &new_term_settings); + + // The slave side of the PTY becomes the standard input and outputs of the child process + // we use cook mode here + close(0); // Close standard input (current terminal) + close(1); // Close standard output (current terminal) + close(2); // Close standard error (current terminal) + + rc = dup(fds); // PTY becomes standard input (0) + rc = dup(fds); // PTY becomes standard output (1) + rc = dup(fds); // PTY becomes standard error (2) + + // Now the original file descriptor is useless + close(fds); + + // Make the current process a new session leader + setsid(); + + // As the child is a session leader, set the controlling terminal to be the slave side of the PTY + // (Mandatory for programs like the shell to make them manage correctly their outputs) + ioctl(0, TIOCSCTTY, 1); + + //system("/bin/bash"); + rc = system("TERM=linux login"); + //M_LOG("%s\n","Terminal exit"); + _exit(1); + } +} + +static void terminal_kill(int client_id, int should_delete) +{ + // find the proc + bst_node_t* node = bst_find(processes, client_id); + vterm_proc_t* proc; + if(node != NULL) + { + proc = (vterm_proc_t*)node->data; + if(proc != NULL) + { + (void) close(proc->fdm); + M_LOG(MODULE_NAME, "Kill the process %d", proc->pid); + if(kill(proc->pid, SIGKILL) == - 1) + { + M_ERROR(MODULE_NAME, "Unable to kill process %d: %s", proc->pid, strerror(errno)); + } + free(node->data); + if(should_delete) + processes = bst_delete(processes, node->key); + } + } +} + +static int terminal_write(tunnel_msg_t* msg) +{ + // TODO: control frame e.g. for window resize + bst_node_t* node = bst_find(processes, msg->header.client_id); + vterm_proc_t* proc; + if(node != NULL) + { + proc = (vterm_proc_t*)node->data; + if(proc != NULL) + { + if(write(proc->fdm, msg->data, msg->header.size) == -1) + { + M_ERROR(MODULE_NAME, "Unable to write data to the terminal corresponding to client %d", msg->header.client_id); + return -1; + } + } + else + { + M_ERROR(MODULE_NAME, "Unable to find the process linked to client %d", msg->header.client_id); + return -1; + } + } + else + { + M_ERROR(MODULE_NAME, "Unable to find the process from processes list for %d", msg->header.client_id); + return -1; + } + return 0; +} + +static void unsubscribe(bst_node_t* node, void** args, int argc) +{ + (void) argc; + tunnel_msg_t msg; + int* ufd = (int*) args[0]; + vterm_proc_t* proc = (vterm_proc_t*) node->data; + if(proc != NULL) + { + msg.header.type = CHANNEL_UNSUBSCRIBE; + msg.header.client_id = proc->cid; + msg.header.size = 0; + terminal_kill(proc->cid, 0); + if(msg_write(*ufd, &msg) == -1) + { + M_ERROR(MODULE_NAME, "Unable to request unsubscribe to client %d", proc->cid); + } + } +} + +static void set_sock_fd(bst_node_t* node, void** args, int argc) +{ + (void) argc; + tunnel_msg_t msg; + pid_t wpid; + fd_set* fd_in = (fd_set*) args[1]; + int* max_fd = (int*)args[2]; + list_t* list_p = (list_t*) args[3]; + int* ufd = (int*) args[0]; + + vterm_proc_t* proc = (vterm_proc_t*) node->data; + + if(proc != NULL) + { + // monitor the pid + wpid = waitpid(proc->pid, NULL, WNOHANG); + if(wpid == -1 || wpid > 0) + { + // child exits + M_LOG(MODULE_NAME, "Terminal linked to client %d exits\n", proc->cid); + unsubscribe(node, args, argc); + list_put_ptr(list_p, node); + } + else + { + FD_SET(proc->fdm, fd_in); + if(*max_fd < proc->fdm) + { + *max_fd = proc->fdm; + } + } + } +} + +static void terminal_monitor(bst_node_t* node, void** args, int argc) +{ + (void) argc; + int* ufd = (int*) args[0]; + fd_set* fd_in = (fd_set*) args[1]; + list_t* list = (list_t*) args[3]; + char buff[BUFFLEN]; + tunnel_msg_t msg; + int rc; + vterm_proc_t* proc = (vterm_proc_t*) node->data; + + if(proc != NULL && FD_ISSET(proc->fdm, fd_in)) + { + if ((rc = read(proc->fdm, buff, BUFFLEN)) > 0) + { + // Send data to client + msg.header.client_id = node->key; + msg.header.type = CHANNEL_DATA; + msg.header.size = rc; + msg.data = buff; + if(msg_write(*ufd, &msg) == -1) + { + terminal_kill(node->key, 0); + M_ERROR(MODULE_NAME,"Unable to send data to client %d", msg.header.client_id); + list_put_ptr(list, node); + } + } + else + { + if (rc < 0) + { + M_LOG(MODULE_NAME, "Error on read standard input: %s\n", strerror(errno)); + terminal_kill(node->key, 0); + list_put_ptr(list, node); + } + } + } +} + +static void terminal_resize(int cid, int col, int row) +{ + struct winsize win = {0, 0, 0, 0}; + bst_node_t* node = bst_find(processes, cid); + vterm_proc_t* proc; + if(node != NULL) + { + proc = (vterm_proc_t*) node->data; + if (ioctl(proc->fdm, TIOCGWINSZ, &win) != 0) + { + if (errno != EINVAL) + { + M_ERROR(MODULE_NAME, "Unable to get terminal winsize setting: %s", strerror(errno)); + return; + } + memset(&win, 0, sizeof(win)); + } + //printf("Setting winsize\n"); + if (row >= 0) + win.ws_row = (unsigned short)row; + if (col >= 0) + win.ws_col = (unsigned short)col; + + if (ioctl(proc->fdm, TIOCSWINSZ, (char *)&win) != 0) + M_ERROR(MODULE_NAME, "Unable to set terminal window size process linked to client %d: %s", cid, strerror(errno)); + } + else + { + M_ERROR(MODULE_NAME, "Unable to find the terminal process linked to client %d", cid); + } +} int main(int argc, char** argv) { int fd; tunnel_msg_t msg; fd_set fd_in; - int status; + int status, maxfd; struct timeval timeout; char buff[MAX_CHANNEL_NAME+1]; + void *args[4]; + list_t list; + item_t item; + int ncol, nrow; + LOG_INIT(MODULE_NAME); if(argc != 2) { @@ -32,38 +314,38 @@ int main(int argc, char** argv) return -1; } signal(SIGINT, int_handler); - LOG(MODULE_NAME, "Hotline is: %s", argv[1]); + M_LOG(MODULE_NAME, "Hotline is: %s", argv[1]); // now try to request new channel from hotline fd = open_unix_socket(argv[1]); if(fd == -1) { - ERROR(MODULE_NAME, "Unable to open the hotline: %s", argv[1]); + M_ERROR(MODULE_NAME, "Unable to open the hotline: %s", argv[1]); return -1; } msg.header.type = CHANNEL_OPEN; msg.header.channel_id = 0; msg.header.client_id = 0; - LOG(MODULE_NAME, "Request to open the channel %s", MODULE_NAME); + M_LOG(MODULE_NAME, "Request to open the channel %s", MODULE_NAME); (void)strncpy(buff, MODULE_NAME,MAX_CHANNEL_NAME); msg.header.size = strlen(buff); msg.data = (uint8_t*) buff; if(msg_write(fd, &msg) == -1) { - ERROR(MODULE_NAME, "Unable to write message to hotline"); + M_ERROR(MODULE_NAME, "Unable to write message to hotline"); (void) close(fd); return -1; } - LOG(MODULE_NAME, "Wait for comfirm creation of %s", MODULE_NAME); + M_LOG(MODULE_NAME, "Wait for comfirm creation of %s", MODULE_NAME); // now wait for message if(msg_read(fd, &msg) == -1) { - ERROR(MODULE_NAME, "Unable to read message from hotline"); + M_ERROR(MODULE_NAME, "Unable to read message from hotline"); (void) close(fd); return -1; } if(msg.header.type == CHANNEL_OK) { - LOG(MODULE_NAME, "Channel created: %s", MODULE_NAME); + M_LOG(MODULE_NAME, "Channel created: %s", MODULE_NAME); if(msg.data) free(msg.data); } @@ -75,12 +357,28 @@ int main(int argc, char** argv) timeout.tv_usec = 500; FD_ZERO(&fd_in); FD_SET(fd, &fd_in); - status = select(fd + 1, &fd_in, NULL, NULL, &timeout); + maxfd = fd; + + // monitor processes + list = list_init(); + args[1] = (void*) &fd_in; + args[2] = (void*) &maxfd; + args[3] = (void*) &list; + args[0] = (void*) &fd; + bst_for_each(processes, set_sock_fd, args, 4); + list_for_each(item, list) + { + processes = bst_delete(processes, ((bst_node_t*)(item->value.ptr))->key); + item->value.ptr = NULL; + } + list_free(&list); + + status = select(maxfd + 1, &fd_in, NULL, NULL, &timeout); switch (status) { case -1: - LOG(MODULE_NAME, "Error %d on select()\n", errno); + M_LOG(MODULE_NAME, "Error %d on select()\n", errno); running = 0; break; case 0: @@ -90,47 +388,118 @@ int main(int argc, char** argv) break; // we have data default: - if(msg_read(fd, &msg) == -1) + if (FD_ISSET(fd, &fd_in)) { - ERROR(MODULE_NAME, "Unable to read message from channel. quit"); - (void) close(fd); - running = 0; + if(msg_read(fd, &msg) == -1) + { + M_ERROR(MODULE_NAME, "Unable to read message from channel. quit"); + (void) close(fd); + running = 0; + } + else + { + switch (msg.header.type) + { + case CHANNEL_SUBSCRIBE: + M_LOG(MODULE_NAME, "Client %d subscribes to the chanel", msg.header.client_id); + // create new process + vterm_proc_t* proc = terminal_new(); + if(proc == NULL) + { + M_ERROR(MODULE_NAME, "Unable to create new terminal for client %d", msg.header.client_id); + // unsubscribe client + msg.header.type = CHANNEL_UNSUBSCRIBE; + msg.header.size = 0; + if(msg_write(fd, &msg) == -1) + { + M_LOG(MODULE_NAME,"Unable to request unsubscribe client %d", msg.header.client_id); + } + } + else + { + proc->cid = msg.header.client_id; + // insert new terminal to the list + processes = bst_insert(processes, msg.header.client_id, proc); + } + break; + + case CHANNEL_UNSUBSCRIBE: + M_LOG(MODULE_NAME, "Client %d unsubscribes to the chanel", msg.header.client_id); + terminal_kill(msg.header.client_id, 1); + break; + + case CHANNEL_CTRL: + if(msg.header.size == 8) + { + (void)memcpy(&ncol, msg.data, sizeof(ncol)); + (void)memcpy(&nrow, msg.data + sizeof(ncol), sizeof(nrow)); + M_LOG(MODULE_NAME, "Client %d request terminal window resize of (%d,%d)", msg.header.client_id, ncol, nrow); + terminal_resize(msg.header.client_id, ncol, nrow); + + } + else + { + M_ERROR(MODULE_NAME, "Invalid control message size: %d from client %d, expected 8", msg.header.size, msg.header.client_id); + } + + break; + + case CHANNEL_DATA: + if(terminal_write(&msg) == -1) + { + M_ERROR(MODULE_NAME, "Unable to write data to terminal corresponding to client %d", msg.header.client_id); + terminal_kill(msg.header.client_id, 1); + msg.header.type = CHANNEL_UNSUBSCRIBE; + msg.header.size = 0; + if(msg_write(fd, &msg) == -1) + { + M_LOG(MODULE_NAME,"Unable to request unsubscribe client %d", msg.header.client_id); + } + } + break; + + default: + M_LOG(MODULE_NAME, "Client %d send message of type %d", + msg.header.client_id, msg.header.type); + break; + } + if(msg.data) + { + free(msg.data); + } + } } else { - switch (msg.header.type) + // on the processes side + list = list_init(); + bst_for_each(processes, terminal_monitor, args, 4); + list_for_each(item, list) { - case CHANNEL_SUBSCRIBE: - LOG(MODULE_NAME, "Client %d subscribes to the chanel", msg.header.client_id); - break; - - case CHANNEL_UNSUBSCRIBE: - LOG(MODULE_NAME, "Client %d unsubscribes to the chanel", msg.header.client_id); - break; - case CHANNEL_DATA: - LOG(MODULE_NAME, "Got data"); - if(msg_write(fd, &msg) == -1) - { - LOG(MODULE_NAME,"Unable to write data back"); - } - break; - default: - LOG(MODULE_NAME, "Client %d send message of type %d", msg.header.client_id, msg.header.type); - break; + processes = bst_delete(processes, ((bst_node_t*)(item->value.ptr))->key); + item->value.ptr = NULL; } + list_free(&list); } + } } + // unsubscribe all clients + args[0] = (void*) &fd; + bst_for_each(processes, unsubscribe, args, 1); + (void)bst_free(processes); // close the channel - LOG(MODULE_NAME, "Close the channel %s (%d)", MODULE_NAME, fd); + M_LOG(MODULE_NAME, "Close the channel %s (%d)", MODULE_NAME, fd); msg.header.type = CHANNEL_CLOSE; msg.header.size = 0; msg.data = NULL; if( msg_write(fd, &msg) == -1) { - ERROR(MODULE_NAME, "Unable to request channel close"); + M_ERROR(MODULE_NAME, "Unable to request channel close"); } + // close all opened terminal + (void)msg_read(fd, &msg); (void) close(fd); return 0;