diff options
author | Andreas Baumann <mail@andreasbaumann.cc> | 2014-12-03 11:23:49 +0100 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2014-12-03 11:23:49 +0100 |
commit | 39922d9f6fd32497f872dc4605c10b6187addf8b (patch) | |
tree | 61fd37009024eedc5e47f34b555adc5d4703deb2 /src/cli.c | |
parent | 6a01f50211d679522f60a7d783c381e377f21ccb (diff) | |
download | biruda-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.c | 311 |
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; +} |