480 lines
15 KiB
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;
|
|
}
|