MASA-Core
MasaNetCLI.cpp
Go to the documentation of this file.
00001 /*******************************************************************************
00002  *
00003  * Copyright (c) 2010-2015   Edans Sandes
00004  *
00005  * This file is part of MASA-Core.
00006  * 
00007  * MASA-Core is free software: you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License as published by
00009  * the Free Software Foundation, either version 3 of the License, or
00010  * (at your option) any later version.
00011  * 
00012  * MASA-Core is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU General Public License
00018  * along with MASA-Core.  If not, see <http://www.gnu.org/licenses/>.
00019  *
00020  ******************************************************************************/
00021 
00022 #include "MasaNetCLI.hpp"
00023 
00024 #include <stdio.h>
00025 #include <stdlib.h>
00026 #include <unistd.h>
00027 #include <string.h>
00028 #include <readline/readline.h>
00029 #include <readline/history.h>
00030 
00031 /*
00032  * Default prompt.
00033  */
00034 #define DEFAULT_PROMPT          "masa-cli# "
00035 
00036 
00037 #define WELLCOME_MESSAGE        "\n\
00038 This is the \033[1mMASA\033[0m Command Line Interface (masa-cli).\n\
00039 Type \"help\" or ? for further assistance\n\
00040 \n"
00041 
00042 /*
00043  * Allow parsing of the ~/.inputrc file .
00044  */
00045 #define READLINE_NAME           "masa"
00046 
00047 
00048 
00049 command_t MasaNetCLI::commands[] = {
00050   { "connect",  MasaNetCLI::cmd_connect,        "Starts a new connection with the given hostname[:port]" },
00051   { "show status",      MasaNetCLI::cmd_status,         "Shows the current connection status" },
00052   { "show discovered",  MasaNetCLI::cmd_show_discovered,        "Shows the discovered peers" },
00053   { "show connected",   MasaNetCLI::cmd_show_connected,         "Shows the connected peers" },
00054   { "show ring",        MasaNetCLI::cmd_show_ring,      "Shows the data peers in the ring" },
00055   { "test ring",        MasaNetCLI::cmd_test_ring,      "Test all the peers in the ring" },
00056   { "create ring",      MasaNetCLI::cmd_create_ring,    "Creates the buffers ring" },
00057 
00058   { "align local",      (command_f)NULL,        "" },
00059 
00060   { "help",     MasaNetCLI::cmd_help,           "Lists the commands help" },
00061   { "quit",     MasaNetCLI::cmd_quit,           "Quits the CLI" },
00062   { (char *)NULL, (command_f)NULL, (char *)NULL },
00063 };
00064 
00065 
00066 
00067 MasaNet* MasaNetCLI::masaNet = NULL;
00068 
00069 void MasaNetCLI::initialize() {
00070         if (masaNet == NULL) {
00071                 masaNet = new MasaNet(TYPE_CLI, "Command Line Interface");
00072         }
00073 }
00074 
00075 void MasaNetCLI::openConsole() {
00076 
00077         /* Allow conditional parsing of the ~/.inputrc file. */
00078         rl_readline_name = READLINE_NAME;
00079 
00080         /* Tell the completer that we want a crack first. */
00081         rl_attempted_completion_function = console_completion;
00082 
00083     char* input;
00084     char shell_prompt[100];
00085 
00086     printf(WELLCOME_MESSAGE);
00087 
00088     // Create prompt string from user name and current working directory.
00089     snprintf(shell_prompt, sizeof(shell_prompt), DEFAULT_PROMPT);
00090 
00091     // Configure readline to auto-complete paths when the tab/? keys are hit.
00092     //rl_bind_key('\t', rl_complete);
00093     //rl_bind_key('?', rl_complete);
00094 
00095     while (1) {
00096 
00097 
00098         // Display prompt and read input (n.b. input must be freed after use)...
00099         input = readline(shell_prompt);
00100         // Check for EOF.
00101         if (!input) {
00102                 //printf("\n");
00103             break;
00104         }
00105 
00106         char* s = strip_whitespaces(input);
00107         if (*s) {
00108                 // Add input to history.
00109                 add_history(s);
00110                 if (execute_line(s) == CMD_ABORT) {
00111                         return;
00112                 }
00113         }
00114         // Do stuff...
00115 
00116         // Free input.
00117         free(input);
00118     }
00119 
00120 
00121         sleep(500);
00122 }
00123 
00124 
00125 
00126 
00127 
00128 /* Execute a command line. */
00129 int MasaNetCLI::execute_line (char *line) {
00130         register int i;
00131         command_t *command;
00132         char *word;
00133 
00134         /* Isolate the command word. */
00135         i = 0;
00136         while (line[i] && whitespace (line[i])) {
00137                 i++;
00138         }
00139         word = line + i;
00140 
00141         //if (line[i])
00142         //line[i++] = '\0';
00143 
00144         command = find_command(line);
00145 
00146         if (!command) {
00147                 fprintf(stderr, "%s: No such command for FileMan.\n", word);
00148                 return CMD_ERROR;
00149         }
00150 
00151         i += strlen(command->name);
00152         while (line[i] && whitespace (line[i])) {
00153                 i++;
00154         }
00155         word = line + i;
00156 
00157         /* Call the function. */
00158         return ((*(command->func)) (word));
00159 }
00160 
00161 
00162 /* Look up NAME as the name of a command, and return a pointer to that
00163    command.  Return a NULL pointer if NAME isn't a command name. */
00164 command_t* MasaNetCLI::find_command (char *name) {
00165         register int i;
00166 
00167         for (i = 0; commands[i].name; i++) {
00168                 int len = strlen(commands[i].name);
00169                 if (strncmp(name, commands[i].name, len) == 0) {
00170                         if (name[len] == '\0' || whitespace(name[len])) {
00171                                 return (&commands[i]);
00172                         }
00173                 }
00174         }
00175         return ((command_t *) NULL);
00176 }
00177 
00178 /* Strip whitespace from the start and end of STRING.  Return a pointer
00179    into STRING. */
00180 char *MasaNetCLI::strip_whitespaces (char * string) {
00181 
00182   register char *s, *t;
00183 
00184   for (s = string; whitespace (*s); s++)
00185     ;
00186 
00187   if (*s == 0)
00188     return (s);
00189 
00190   t = s + strlen (s) - 1;
00191   while (t > s && whitespace (*t))
00192     t--;
00193   *++t = '\0';
00194 
00195   return s;
00196 }
00197 
00198 
00199 /* Attempt to complete on the contents of TEXT.  START and END
00200    bound the region of rl_line_buffer that contains the word to
00201    complete.  TEXT is the word to complete.  We can use the entire
00202    contents of rl_line_buffer in case we want to do some simple
00203    parsing.  Returnthe array of matches, or NULL if there aren't any. */
00204 char ** MasaNetCLI::console_completion (const char *text, int start, int end) {
00205   char **matches;
00206 
00207   //printf("%s\n", rl_line_buffer);
00208 
00209   matches = (char **)NULL;
00210   /* If this word is at the start of the line, then it is a command
00211      to complete.  Otherwise it is the name of a file in the current
00212      directory. */
00213   //if (start == 0)
00214     matches = rl_completion_matches (text, command_generator);
00215   //rl_attempted_completion_over=true;
00216 
00217   //printf("\n%s %s %s %d %d\n\n", rl_line_buffer, matches[0], text, start, end);
00218 
00219   return (matches);
00220 }
00221 
00222 /* Generator function for command completion.  STATE lets us
00223    know whether to start from scratch; without any state
00224    (i.e. STATE == 0), then we start at the top of the list. */
00225 char * MasaNetCLI::command_generator (const char *text, int state) {
00226 
00227   static int list_index, len;
00228   char *name;
00229 
00230   /* If this is a new word to complete, initialize now.  This
00231      includes saving the length of TEXT for efficiency, and
00232      initializing the index variable to 0. */
00233   if (!state)
00234     {
00235       list_index = 0;
00236       len = strlen (rl_line_buffer);
00237     }
00238 
00239   /* Return the next name which partially matches from the
00240      command list. */
00241   while (name = commands[list_index].name)
00242     {
00243       list_index++;
00244 
00245       if (len == 0 || strncmp (name, rl_line_buffer, len) == 0) {
00246           int p = len-1;
00247           while (name[p] != ' ' && p != 0) {
00248                 p--;
00249           }
00250           if (name[p] == ' ') p++;
00251           char* ptr = &name[p];
00252           char* r = (char*)malloc (strlen (ptr) + 1);
00253 
00254           strcpy (r, ptr);
00255           //printf(".%s\n", r);
00256           return r;
00257       }
00258     }
00259 
00260   /* If no names matched, then return NULL. */
00261   return ((char *)NULL);
00262 }
00263 
00264 
00265 void MasaNetCLI::printTopology(int type) {
00266         vector<Peer*> peers = masaNet->getConnectedPeers().getAllPeers();
00267         vector< vector<Peer*> > topology;
00268 
00269         for (vector<Peer*>::const_iterator it = peers.begin() ; it != peers.end(); ++it) {
00270                 Peer* src = (*it);
00271                 vector<Peer*> v;
00272                 v.push_back(src);
00273 
00274                 vector<Peer*> remote_peers = masaNet->getRemotePeers(src->getRemoteId().c_str(), type);
00275                 for (vector<Peer*>::const_iterator it2 = remote_peers.begin() ; it2 != remote_peers.end(); ++it2) {
00276                         Peer* dst = (*it2);
00277                         v.push_back(dst);
00278                 }
00279                 topology.push_back(v);
00280         }
00281 
00282         for (vector< vector<Peer*> >::const_iterator it = topology.begin() ; it != topology.end(); ) {
00283                 vector<Peer*> v = *it;
00284                 Peer* src = v.front();
00285                 if (it != topology.begin()) {
00286                         printf("|\n");
00287                 }
00288                 it++;
00289                 printf("+-+- %s:\n", src->toString().c_str());
00290                 if (v.size() > 1) {
00291                         char space = ' ';
00292                         if (it != topology.end()) {
00293                                 space = '|';
00294                         }
00295                         printf("%c |\n", space);
00296                         for (vector<Peer*>::const_iterator it2 = v.begin()+1 ; it2 != v.end(); ++it2) {
00297                                 Peer* dst = *it2;
00298 
00299                                 printf("%c +----- %s\n", space, dst->toString().c_str());
00300                         }
00301                 }
00302         }
00303 
00304 }
00305 
00306 
00307 
00308 
00309 int MasaNetCLI::cmd_connect(char* arg) {
00310         if (strcmp(arg, "all") == 0) {
00311                 vector<Peer*> peers = masaNet->getConnectedPeers().getProcessingPeers();
00312                 set<string> newPeers;
00313 
00314                 for (vector<Peer*>::const_iterator it = peers.begin() ; it != peers.end(); ++it) {
00315                         vector<Peer*> remote_peers = masaNet->getRemotePeers((*it)->getRemoteId().c_str(), CMD_DISCOVERED_PEERS);
00316                         for (vector<Peer*>::const_iterator it2 = remote_peers.begin() ; it2 != remote_peers.end(); ++it2) {
00317                                 if (masaNet->getConnectedPeers().get((*it2)->getRemoteId()) == NULL) {
00318                                         newPeers.insert((*it2)->getRemoteAddress());
00319                                 }
00320                         }
00321                 }
00322 
00323                 for (set<string>::const_iterator it = newPeers.begin(); it != newPeers.end(); ++it) {
00324                         printf("Should connect to %s\n", (*it).c_str());
00325                         masaNet->connectToPeer(*it, CONNECTION_TYPE_CTRL);
00326                 }
00327                 return CMD_OK;
00328         } else {
00329                 Peer* ret = masaNet->connectToPeer(arg, CONNECTION_TYPE_CTRL);
00330                 ret->waitHandshake();
00331                 if (ret != NULL) {
00332                         //printf("Connection Established.\n");
00333                         return CMD_OK;
00334                 } else {
00335                         //printf("Connection Error.\n");
00336                         return CMD_ERROR;
00337                 }
00338         }
00339 
00340 
00341 }
00342 
00343 /* The user wishes to quit using this program.  Just set DONE
00344    non-zero. */
00345 int MasaNetCLI::cmd_quit (char *arg) {
00346   exit(0);
00347   return CMD_OK;
00348 }
00349 
00350 
00351 int MasaNetCLI::cmd_status(char* arg) {
00352         char host[256];
00353         char pstr[16];
00354         int port = 0;
00355         sscanf(arg, "%[^:]:%d", host, &port);
00356         sprintf(pstr, "%d", port);
00357         MasaNetStatus* status = masaNet->getPeerStatus();
00358 
00359         printf("\n%s\n", status->toString().c_str());
00360         //masaNet->waitConnected();
00361         return CMD_OK;
00362 }
00363 
00364 
00365 int MasaNetCLI::cmd_show_connected(char* arg) {
00366 
00367         printTopology(CMD_CONNECTED_PEERS);
00368 
00369         return CMD_OK;
00370 }
00371 
00372 int MasaNetCLI::cmd_show_connected_remote(char* arg) {
00373         return CMD_OK;
00374 }
00375 
00376 int MasaNetCLI::cmd_show_discovered(char* arg) {
00377         printTopology(CMD_DISCOVERED_PEERS);
00378 
00379         return CMD_OK;
00380 }
00381 
00382 int MasaNetCLI::cmd_show_ring(char* arg) {
00383         printTopology(CMD_DATA_PEERS);
00384 
00385         return CMD_OK;
00386 }
00387 
00388 int MasaNetCLI::cmd_create_ring(char* arg) {
00389         masaNet->createRing();
00390 
00391         return CMD_OK;
00392 }
00393 
00394 
00395 int MasaNetCLI::cmd_test_ring(char* arg) {
00396         const set<string>& ids = masaNet->testRing(arg);
00397         printf("Ring size: %d\n", ids.size());
00398         int count = 0;
00399         for (std::set<string>::iterator it=ids.begin(); it!=ids.end(); ++it) {
00400                 printf("%3d: %s\n", count++, (*it).c_str());
00401         }
00402 
00403         return CMD_OK;
00404 }
00405 
00406 /* Print out help for ARG, or for all of the commands if ARG is
00407    not present. */
00408 int MasaNetCLI::cmd_help (char *arg) {
00409   register int i;
00410   int printed = 0;
00411 
00412   for (int  i = 0; commands[i].name; i++)
00413     {
00414       if (!*arg || (strcmp (arg, commands[i].name) == 0))
00415         {
00416           printf ("%s\t\t%s.\n", commands[i].name, commands[i].doc);
00417           printed++;
00418         }
00419     }
00420 
00421   if (!printed)
00422     {
00423       printf ("No commands match `%s'.  Possibilties are:\n", arg);
00424 
00425       for (i = 0; commands[i].name; i++)
00426         {
00427           /* Print in six columns. */
00428           if (printed == 6)
00429             {
00430               printed = 0;
00431               printf ("\n");
00432             }
00433 
00434           printf ("%s\t", commands[i].name);
00435           printed++;
00436         }
00437 
00438       if (printed)
00439         printf ("\n");
00440     }
00441   return CMD_OK;
00442 }
00443 
00444 
00445 
00446 int main(int argc, char** argv) {
00447         if (argc > 2) {
00448                 fprintf(stderr, "usage: %s [host[:port]]\n", argv[0]);
00449                 exit(1);
00450         }
00451         MasaNetCLI::initialize();
00452         if (argc == 2) {
00453                 MasaNetCLI::cmd_connect(argv[1]);
00454         }
00455         MasaNetCLI::openConsole();
00456 }
00457