summaryrefslogtreecommitdiff
path: root/src/cli.c
diff options
context:
space:
mode:
authorAndreas Baumann <mail@andreasbaumann.cc>2014-12-03 11:23:49 +0100
committerAndreas Baumann <mail@andreasbaumann.cc>2014-12-03 11:23:49 +0100
commit39922d9f6fd32497f872dc4605c10b6187addf8b (patch)
tree61fd37009024eedc5e47f34b555adc5d4703deb2 /src/cli.c
parent6a01f50211d679522f60a7d783c381e377f21ccb (diff)
downloadbiruda-39922d9f6fd32497f872dc4605c10b6187addf8b.tar.gz
biruda-39922d9f6fd32497f872dc4605c10b6187addf8b.tar.bz2
moved cli part of biruda to a separate module
Diffstat (limited to 'src/cli.c')
-rw-r--r--src/cli.c311
1 files changed, 311 insertions, 0 deletions
diff --git a/src/cli.c b/src/cli.c
new file mode 100644
index 0000000..3f2e7fd
--- /dev/null
+++ b/src/cli.c
@@ -0,0 +1,311 @@
+#include "cli.h"
+
+#ifndef _WIN32
+#include <strings.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include "linenoise.h"
+#include "http_lib.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#define HISTORY_FILE ".biruda_history"
+
+#include "coordinator.h"
+#include "worker.h"
+
+typedef enum {
+ COMMAND, // command parsing
+ START_WORKER, // worker command expecting a worker parameter
+ STOP_WORKER
+} command_state_t;
+
+static char *commands[] = {
+ "help", "quit", "status", "start", "stop", NULL
+};
+
+static bool print_colors = false;
+
+static bool is_interactive = false;
+
+static command_state_t command_state = COMMAND;
+
+static char *worker_names[MAX_WORKERS];
+
+static int nof_worker_names = 0;
+
+static void cleanup_worker_names( )
+{
+ for( int i = 0; i < nof_worker_names; i++ ) {
+ free( worker_names[i] );
+ }
+ nof_worker_names = 0;
+}
+
+static void print_error( const char *fmt, ... );
+
+static void get_workers( )
+{
+ char *url = "status";
+ char *data = NULL;
+ int len;
+ http_retcode ret = http_get( url, &data, &len, NULL );
+ if( ret == 200 ) {
+ cleanup_worker_names( );
+
+ char *line = strtok( data, "\n" );
+ while( line != NULL ) {
+ char *p = strchr( line, ' ' );
+ if( p == NULL ) {
+ line = strtok( NULL, "\n" );
+ continue;
+ }
+ if( strncmp( line, "worker", p - line ) != 0 ) {
+ line = strtok( NULL, "\n" );
+ continue;
+ }
+ char *pp = strchr( p+1, ' ' );
+ if( pp == NULL ) {
+ line = strtok( NULL, "\n" );
+ continue;
+ }
+ char s = *pp;
+ *pp = '\0';
+ if( nof_worker_names >= MAX_WORKERS ) {
+ fprintf( stderr, "ERROR: Too many workers seen, ignoring rest!\n" );
+ free( data );
+ return;
+ }
+ worker_names[nof_worker_names++] = strdup( p + 1 );
+ *pp = s;
+ line = strtok( NULL, "\n" );
+ }
+ } else {
+ print_error( "ERROR: HTTP error %d", ret );
+ }
+ free( data );
+}
+
+static void completion_func( const char *buf, linenoiseCompletions *lc )
+{
+ unsigned int i;
+ size_t len = strlen( buf );
+
+ switch( command_state ) {
+ case COMMAND:
+ for (i = 0; commands[i]; ++i) {
+ char *cmd = commands[i];
+ if( strlen( cmd ) < len ) continue;
+ if( strncasecmp( buf, cmd, len ) == 0 ) {
+ linenoiseAddCompletion( lc, cmd );
+ }
+ }
+ break;
+
+ case START_WORKER:
+ case STOP_WORKER:
+ get_workers( );
+ for( int i = 0; i < nof_worker_names; i++ ) {
+ linenoiseAddCompletion( lc, worker_names[i] );
+ }
+ break;
+ }
+}
+
+static void print_help( )
+{
+ puts( "\n"
+ " help - show this help page\n"
+ " quit - quit the client\n"
+ " status - status of the biruda network\n"
+ " start - start a worker manually\n"
+ " stop - stop a worker manually\n"
+ );
+}
+
+static void print_colored( const char *fmt, int color, va_list ap )
+{
+ char buf[1024];
+
+ (void)vsnprintf( buf, sizeof( buf ), fmt, ap );
+
+ if( print_colors ) {
+ printf( "%c[9%dm%s%c[0m\n", 27, color, buf, 27 );
+ } else {
+ puts( buf );
+ }
+}
+
+static void print_error( const char *fmt, ... )
+{
+ va_list ap;
+ va_start( ap, fmt );
+ print_colored( fmt, 1, ap );
+ va_end( ap );
+}
+
+static void print_answer( const char *fmt, ... )
+{
+ va_list ap;
+ va_start( ap, fmt );
+ print_colored( fmt, 2, ap );
+ va_end( ap );
+}
+
+static void print_status( )
+{
+ char *url = "status";
+ char *data = NULL;
+ int len;
+ http_retcode ret = http_get( url, &data, &len, NULL );
+ if( ret == 200 ) {
+ if( strlen( data ) > 0 && data[len-1] == '\n' ) {
+ data[len-1] = '\0';
+ len--;
+ }
+ if( strlen( data ) > 0 && data[len-1] == '\r' ) {
+ data[len-1] = '\0';
+ }
+ print_answer( data );
+ } else {
+ print_error( "ERROR: HTTP error %d", ret );
+ }
+ free( data );
+}
+
+static void start_worker( const char *worker_name )
+{
+ char url[128];
+ snprintf( url, sizeof( url ), "worker?op=start&name=%s", worker_name );
+ http_retcode ret = http_post( url, "", 0, "Content-Type: text/plain" );
+ if( ret == 200 ) {
+ print_answer( "Request queued" );
+ } else {
+ print_error( "ERROR: HTTP error %d", ret );
+ }
+}
+
+static void stop_worker( const char *worker_name )
+{
+ char url[128];
+ snprintf( url, sizeof( url ), "worker?op=stop&name=%s", worker_name );
+ http_retcode ret = http_post( url, "", 0, "Content-Type: text/plain" );
+ if( ret == 200 ) {
+ print_answer( "Request queued" );
+ } else {
+ print_error( "ERROR: HTTP error %d", ret );
+ }
+}
+
+int start_interactive( bool colors )
+{
+ char history_filename[1024];
+
+ // for http_tidy, tell it where to issue requests to
+ http_server = "localhost";
+ http_port = 8080;
+
+ is_interactive = isatty( fileno( stdin ) );
+ print_colors = is_interactive ? colors : false;
+
+ if( is_interactive ) {
+ char *home = getenv( "HOME" );
+ if( home != NULL ) {
+ snprintf( history_filename, sizeof( history_filename ), "%s/%s", home, HISTORY_FILE );
+ linenoiseHistoryLoad( history_filename );
+ linenoiseSetCompletionCallback( completion_func );
+ }
+ }
+
+ char *context = "biruda";
+ for( ;; ) {
+ char prompt[128];
+ char buf[1024];
+ char *line;
+
+ switch( command_state ) {
+ case COMMAND:
+ context = "biruda";
+ break;
+
+ case START_WORKER:
+ case STOP_WORKER:
+ context = "worker";
+ break;
+ }
+
+ if( is_interactive ) {
+ snprintf( prompt, sizeof( prompt ), "%s> ", context );
+ if( ( line = linenoise( prompt ) ) == NULL ) {
+ switch( command_state ) {
+ case COMMAND:
+ cleanup_worker_names( );
+ free( line );
+ return EXIT_SUCCESS;
+
+ case START_WORKER:
+ case STOP_WORKER:
+ command_state = COMMAND;
+ continue;
+ }
+ break;
+ }
+ strncpy( buf, line, sizeof( buf ) );
+ free( line );
+ line = buf;
+ } else {
+ if( fgets( buf, sizeof( buf ), stdin ) == NULL ) {
+ cleanup_worker_names( );
+ return EXIT_SUCCESS;
+ }
+ line = buf;
+ }
+
+ if( strlen( line ) > 1 ) {
+ if( line[strlen( line )-1] == '\n' ) {
+ line[strlen( line )-1] = '\0';
+ }
+ }
+
+ if( is_interactive ) {
+ linenoiseHistoryAdd( line );
+ }
+
+ switch( command_state ) {
+ case COMMAND:
+ if( strncasecmp( line, "quit", 4 ) == 0 ) {
+ if( is_interactive ) {
+ linenoiseHistorySave( history_filename );
+ }
+ cleanup_worker_names( );
+ return EXIT_SUCCESS;
+ } else if( strncasecmp( line, "help", 4 ) == 0 ) {
+ print_help( );
+ } else if( strncasecmp( line, "status", 6 ) == 0 ) {
+ print_status( );
+ } else if( strncasecmp( line, "start", 5 ) == 0 ) {
+ command_state = START_WORKER;
+ } else if( strncasecmp( line, "stop", 5 ) == 0 ) {
+ command_state = STOP_WORKER;
+ } else {
+ print_error( "Bad command '%s'.", line );
+ }
+ break;
+
+ case START_WORKER:
+ start_worker( line );
+ command_state = COMMAND;
+ break;
+
+ case STOP_WORKER:
+ stop_worker( line );
+ command_state = COMMAND;
+ break;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}