gem-graph-server/src/cli.c

480 lines
15 KiB
C

//=-------------------------------------------------------------------------=//
// Command line interface main file //
// //
// Copyright © 2021 The Gem-graph Project //
// //
// This file is part of gem-graph. //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as //
// published by the Free Software Foundation, either version 3 of the //
// License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
//=-------------------------------------------------------------------------=//
#include "../include/base.h"
#include "../include/terminal.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/socket.h>
#define SEND_BUFFER_SIZE 80
#define RECEIVE_BUFFER_SIZE 80 * 24
#define NHISTORY 25
#define SERVER_IP_ADDR "127.0.0.1"
#define SERVER_PORT 9000
#define KEY_ESCAPE_1 27
#define KEY_ESCAPE_2 91
#define KEY_ESCAPE_3 51
#define KEY_ARROW_UP 65 // accessible after sequence KEY_ESCAPE_1, 2
#define KEY_ARROW_DOWN 66 // accessible after sequence KEY_ESCAPE_1, 2
#define KEY_ARROW_RIGHT 67 // accessible after sequence KEY_ESCAPE_1, 2
#define KEY_ARROW_LEFT 68 // accessible after sequence KEY_ESCAPE_1, 2
#define KEY_BACKSPACE 127
#define KEY_DELETE 126 // accessible after sequence KEY_ESCAPE_1, 2, 3
/* -------------------------------------------------------------------------- */
//
// Print monitor screen
//
void CliDecorateMonitor(int signum)
{
const char titleText[] = "GEM-GRAPH MONITOR";
const char screenTitleText[] = "Model view";
const char infosTitleText[] = "Model informations";
const char infosNameText[] = "Name ";
const char infosStatusText[] = "Status ";
const char infosOwnerText[] = "Owner ";
const char infosSchedulerText[] = "Scheduler id";
const char infosScThreadsText[] = "+ threads ";
TermGetScreenSize(signum);
TermClearScreen();
TermSetCursorLocation(1,1);
printf(C_COLOR_NORMAL C_COLOR_WHITE_ON_BLUE);
for (int i = 0; i < TermWindowSize.ws_col; i++) {
printf(" ");
}
TermSetCursorLocation(1,1);
printf("%s%s",
C_COLOR_NORMAL C_COLOR_WHITE_ON_BLUE,
titleText
);
TermSetCursorLocation(1,2);
printf(C_COLOR_NORMAL C_COLOR_BLACK_ON_GREEN);
for (int i = 0; i < TermWindowSize.ws_col; i++) {
printf(" ");
}
TermSetCursorLocation(1,2);
printf("%s%s",
C_COLOR_NORMAL C_COLOR_BLACK_ON_GREEN,
screenTitleText
);
TermSetCursorLocation(TermWindowSize.ws_col / 3 * 2, 2);
printf("%s%s",
C_COLOR_NORMAL C_COLOR_BLACK_ON_GREEN,
infosTitleText
);
TermSetCursorLocation(TermWindowSize.ws_col / 3 * 2, 4);
printf("%s%s%s (void)",
C_COLOR_NORMAL C_COLOR_BLACK_ON_LIGHTGREY,
infosNameText,
C_COLOR_NORMAL
);
TermSetCursorLocation(TermWindowSize.ws_col / 3 * 2, 6);
printf("%s%s%s (void)",
C_COLOR_BLACK_ON_GREEN,
infosStatusText,
C_COLOR_NORMAL
);
TermSetCursorLocation(TermWindowSize.ws_col / 3 * 2, 8);
printf("%s%s%s (void)",
C_COLOR_BLACK_ON_LIGHTGREY,
infosOwnerText,
C_COLOR_NORMAL
);
TermSetCursorLocation(TermWindowSize.ws_col / 3 * 2, 10);
printf("%s%s%s (void)",
C_COLOR_BLACK_ON_GREEN,
infosSchedulerText,
C_COLOR_NORMAL
);
TermSetCursorLocation(TermWindowSize.ws_col / 3 * 2, 12);
printf("%s%s%s (void)",
C_COLOR_BLACK_ON_LIGHTGREY,
infosScThreadsText,
C_COLOR_NORMAL
);
TermSetCursorLocation(1, TermWindowSize.ws_row - 1);
printf(C_COLOR_NORMAL C_COLOR_WHITE_ON_BLUE);
for (int i = 0; i < TermWindowSize.ws_col; i++) {
printf(" ");
}
TermSetCursorLocation(1, TermWindowSize.ws_row);
printf(C_COLOR_NORMAL);
for (int i = 0; i < TermWindowSize.ws_col; i++) {
printf(" ");
}
TermSetCursorLocation(1, TermWindowSize.ws_row);
printf("%sQ%s Quit\t%sS%s Start/Stop",
C_COLOR_NORMAL C_COLOR_REVERSE,
C_COLOR_NORMAL,
C_COLOR_REVERSE,
C_COLOR_NORMAL);
TermSetCursorLocation(1, 3);
fflush(stdout);
}
//
// Monitor mode main loop
//
int CliConnectedMonitor(int sockfd)
{
char sendBuff[SEND_BUFFER_SIZE] = {0};
char receiveBuff[RECEIVE_BUFFER_SIZE] = {0};
int pleaseStop = 0;
char curChar;
int answerLength;
signal(SIGWINCH ,CliDecorateMonitor);
CliDecorateMonitor(0);
while (!pleaseStop) {
// Zeroing buffer
bzero(sendBuff, sizeof(sendBuff));
// Read command from terminal
curChar = TermGetch(NON_BLOCKING);
switch (curChar) {
case 'q':
pleaseStop = true;
break;
}
/* // Otherwise send command to server */
/* send(sockfd, sendBuff, sizeof(sendBuff), 0); */
/* // Zeroing buffer */
/* bzero(receiveBuff, sizeof(receiveBuff)); */
/* // Reading server answer */
/* answerLength = recv(sockfd, receiveBuff, sizeof(receiveBuff), 0); */
/* // Detect disconnection */
/* if (answerLength == 0) { */
/* break; */
/* } */
/* // Detect null-string returned */
/* if (receiveBuff[0] == '\0') { */
/* // Invalid command */
/* } */
}
TermClearScreen();
printf("%sEnd of monitoring session\n\e[0m", C_COLOR_YELLOW);
return 0;
}
//
// Main CLI loop
//
void CliConnectedCommandLine(int sockfd)
{
char sendBuff[SEND_BUFFER_SIZE] = {0};
char receiveBuff[RECEIVE_BUFFER_SIZE] = {0};
char historyBuff[SEND_BUFFER_SIZE * NHISTORY + 1] = {0};
int historyIndex = 0, historyModifier;
char curChar;
int curLineLength, curPosition, continueReadLine, answerLength,
promptLength;
const char *exitCommand = "exit";
const char *monitorCommand = "monitor";
const char *promptString = C_COLOR_BLUE "gem-graph console" C_COLOR_NORMAL "> ";
promptLength = strlen("gem-graph console");
for (;;) {
// Zeroing buffer
bzero(sendBuff, sizeof(sendBuff));
printf("%s", promptString);
// Read command from terminal
curLineLength = 0;
curPosition = 0;
continueReadLine = 1;
historyModifier = -1;
while (continueReadLine && (curChar = TermGetch(0))) {
switch (curChar) {
case '\n':
printf("%c", curChar);
continueReadLine = 0;
break;
case KEY_BACKSPACE:
if (strlen(sendBuff) == 0)
break;
// Delete last char
sendBuff[--curPosition] = 0;
curLineLength--;
if (curLineLength >= 0) {
TermSaveCursorLocation();
memmove(sendBuff + curPosition, sendBuff + curPosition + 1,
SEND_BUFFER_SIZE);
printf("%s\r%s%s",
C_CLEARLINE,
promptString,
sendBuff
);
TermRestoreCursorLocation();
TermMoveCursorBackward();
}
break;
case KEY_ESCAPE_1:
if (TermGetch(0) == KEY_ESCAPE_2) {
switch(TermGetch(0)) {
case KEY_ARROW_UP:
if (historyIndex <= 0)
break;
historyModifier = (historyModifier + 1)
% (historyIndex + 1);
printf("%s\r%s%s",
C_CLEARLINE,
promptString,
&historyBuff[
(historyIndex - historyModifier - 1)
* SEND_BUFFER_SIZE
]
);
memcpy(sendBuff, &historyBuff[
(historyIndex - historyModifier - 1)
* SEND_BUFFER_SIZE
], SEND_BUFFER_SIZE);
curLineLength = strlen(sendBuff);
curPosition = curLineLength;
break;
case KEY_ARROW_DOWN:
if (historyIndex <= 0)
break;
historyModifier = (historyModifier + historyIndex)
% (historyIndex + 1);
printf("%s\r%s%s",
C_CLEARLINE,
promptString,
&historyBuff[
(historyIndex - historyModifier - 1)
* SEND_BUFFER_SIZE
]
);
memcpy(sendBuff, &historyBuff[
(historyIndex - historyModifier - 1)
* SEND_BUFFER_SIZE
], SEND_BUFFER_SIZE);
curLineLength = strlen(sendBuff);
curPosition = curLineLength;
break;
case KEY_ARROW_LEFT:
if (curPosition > 0) {
TermMoveCursorBackward();
curPosition--;
}
break;
case KEY_ARROW_RIGHT:
if (curPosition < curLineLength) {
TermMoveCursorForward();
curPosition++;
}
break;
case KEY_ESCAPE_3:
if (TermGetch(0) == KEY_DELETE) {
if (strlen(sendBuff) == 0)
break;
// Delete next char
sendBuff[curPosition] = 0;
curLineLength--;
if (curLineLength >= 0) {
TermSaveCursorLocation();
memmove(sendBuff + curPosition, sendBuff + curPosition + 1,
SEND_BUFFER_SIZE);
printf("%s\r%s%s",
C_CLEARLINE,
promptString,
sendBuff
);
TermRestoreCursorLocation();
}
else {
TermMoveCursorForward(); // XXX
}
}
break;
}
}
break;
default:
if (curLineLength < MIN(SEND_BUFFER_SIZE,
TermWindowSize.ws_col - promptLength - 3)
&& curChar > 0) {
TermSaveCursorLocation();
memmove(sendBuff + curPosition + 1, sendBuff + curPosition,
SEND_BUFFER_SIZE);
sendBuff[curPosition++] = curChar;
curLineLength++;
printf("%s\r%s%s",
C_CLEARLINE,
promptString,
sendBuff
);
TermRestoreCursorLocation();
TermMoveCursorForward();
}
break;
}
fflush(stdout);
}
// Do not send null-string
if (curLineLength == 0)
continue;
// Update history
memcpy(&historyBuff[historyIndex * SEND_BUFFER_SIZE], sendBuff,
SEND_BUFFER_SIZE);
historyIndex = (historyIndex + 1) % NHISTORY;
// Quit if asked
if (strcmp(exitCommand, sendBuff) == 0) {
break;
}
// Launch monitor mode if asked
if (strcmp(monitorCommand, sendBuff) == 0) {
CliConnectedMonitor(sockfd);
continue;
}
// Otherwise send command to server
send(sockfd, sendBuff, sizeof(sendBuff), 0);
// Zeroing buffer
bzero(receiveBuff, sizeof(receiveBuff));
// Reading server answer
answerLength = recv(sockfd, receiveBuff, sizeof(receiveBuff), 0);
// Detect disconnection
if (answerLength == 0) {
break;
}
// Detect null-string returned
if (receiveBuff[0] == '\0') {
printf("%sInvalid command!\n\e[0m", C_COLOR_YELLOW);
}
printf("%s\n", receiveBuff);
}
printf("%sDisconnected\n\e[0m", C_COLOR_YELLOW);
}
//
// main
//
int main(void)
{
int sockfd;
struct sockaddr_in servaddr;
// Socket creation
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
printLog("%sSocket creation failed! (%s)\n",
C_COLOR_RED,
strerror(errno));
return 1;
}
bzero(&servaddr, sizeof(servaddr));
// Assign IP, PORT
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = inet_addr(SERVER_IP_ADDR);
servaddr.sin_port = htons(SERVER_PORT);
// Connect to the server
if (connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) != 0) {
printLog("%sConnection failed! (%s)\n",
C_COLOR_RED,
strerror(errno));
return 2;
}
printLog("%sConnected to server!\n\n", C_COLOR_GREEN);
signal(SIGWINCH, TermGetScreenSize);
TermGetScreenSize(0);
CliConnectedCommandLine(sockfd);
// close the socket
close(sockfd);
return 0;
}