diff --git a/include/simpleswitcher.h b/include/simpleswitcher.h index 95c9f9f3..da22b224 100644 --- a/include/simpleswitcher.h +++ b/include/simpleswitcher.h @@ -11,6 +11,7 @@ typedef enum { WINDOW_SWITCHER, RUN_DIALOG, + SSH_DIALOG, MODE_EXIT } SwitcherMode; diff --git a/include/ssh-dialog.h b/include/ssh-dialog.h new file mode 100644 index 00000000..0539bc2f --- /dev/null +++ b/include/ssh-dialog.h @@ -0,0 +1,8 @@ +#ifndef __SSH_DIALOG_H__ +#define __SSH_DIALOG_H__ + + + +SwitcherMode ssh_switcher_dialog ( char **input ); + +#endif diff --git a/source/run-dialog.c b/source/run-dialog.c index 0edab990..5ab45f97 100644 --- a/source/run-dialog.c +++ b/source/run-dialog.c @@ -130,7 +130,7 @@ static pid_t exec_cmd( const char *cmd, int run_in_term ) return pid; } -int sort_func ( const void *a, const void *b ) +static int sort_func ( const void *a, const void *b ) { const char *astr = *( const char * const * )a; const char *bstr = *( const char * const * )b; @@ -225,7 +225,7 @@ static char ** get_apps ( ) return retv; } -int token_match ( char **tokens, const char *input, +static int token_match ( char **tokens, const char *input, __attribute__( ( unused ) )int index, __attribute__( ( unused ) )void *data) { @@ -253,7 +253,7 @@ SwitcherMode run_switcher_dialog ( char **input ) int n = menu( cmd_list, input, "$ ", 0, NULL, &shift,token_match, NULL); if ( n == -2 ) { - retv = WINDOW_SWITCHER; + retv = SSH_DIALOG; } else if ( n >=0 && cmd_list[n] != NULL ) { exec_cmd( cmd_list[n], shift ); } else if ( n == -3 && *input != NULL && *input[0] != '\0' ) { diff --git a/source/simpleswitcher.c b/source/simpleswitcher.c index b4c40988..23f48a55 100644 --- a/source/simpleswitcher.c +++ b/source/simpleswitcher.c @@ -59,6 +59,7 @@ #include "simpleswitcher.h" #include "run-dialog.h" +#include "ssh-dialog.h" #define INNER_MARGIN 5 @@ -778,7 +779,7 @@ static int calculate_common_prefix( char **filtered, int max_lines ) } -int window_match ( char **tokens, const char *input, int index, void *data) +int window_match ( char **tokens, __attribute__((unused))const char *input, int index, void *data) { int match =1; winlist *ids = (winlist *)data; @@ -1161,6 +1162,8 @@ void run_switcher( int fmode, SwitcherMode mode ) mode = run_switcher_window( &input ); } else if ( mode == RUN_DIALOG ) { mode = run_switcher_dialog( &input ); + } else if ( mode == SSH_DIALOG ) { + mode = ssh_switcher_dialog( &input ); } } while ( mode != MODE_EXIT ); diff --git a/source/ssh-dialog.c b/source/ssh-dialog.c new file mode 100644 index 00000000..bba2437d --- /dev/null +++ b/source/ssh-dialog.c @@ -0,0 +1,253 @@ +/** + * simpleswitcher + * + * MIT/X11 License + * Copyright 2013-2014 Qball Cow + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define _GNU_SOURCE +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "simpleswitcher.h" +#include "ssh-dialog.h" +#ifdef TIMING +#include +#endif + +extern char *config_terminal_emulator; + +static inline int execshssh( const char *host ) +{ + return execlp( config_terminal_emulator, config_terminal_emulator, "-e", "ssh", host, NULL ); +} +// execute sub-process +static pid_t exec_ssh( const char *cmd ) +{ + if ( !cmd || !cmd[0] ) return -1; + + signal( SIGCHLD, catch_exit ); + pid_t pid = fork(); + + if ( !pid ) { + setsid(); + execshssh( cmd ); + exit( EXIT_FAILURE ); + } + + int curr = -1; + unsigned int index = 0; + char **retv = NULL; + const char *hd = getenv( "HOME" ); + + if ( hd == NULL ) return pid; + + /** + * This happens in non-critical time (After launching app) + * It is allowed to be a bit slower. + */ + char *path = allocate( strlen( hd ) + strlen( "/.simpleswitcher.sshcache" )+2 ); + sprintf( path, "%s/%s", hd, ".simpleswitcher.sshcache" ); + FILE *fd = fopen ( path, "r" ); + char buffer[1024]; + + if ( fd != NULL ) { + while ( fgets( buffer,1024,fd ) != NULL ) { + retv = realloc( retv, ( index+2 )*sizeof( char* ) ); + buffer[strlen( buffer )-1] = '\0'; + retv[index] = strdup( buffer ); + retv[index+1] = NULL; + + if ( strcasecmp( retv[index], cmd ) == 0 ) { + curr = index; + } + + index++; + } + + fclose( fd ); + } + + /** + * Write out the last 25 results again. + */ + fd = fopen ( path, "w" ); + + if ( fd ) { + // Last one goes on top! + fputs( cmd, fd ); + fputc( '\n', fd ); + + for ( int i = 0; i < ( int )index && i < 20; i++ ) { + if ( i != curr ) { + fputs( retv[i], fd ); + fputc( '\n', fd ); + } + } + + fclose( fd ); + } + + for ( int i=0; retv[i] != NULL; i++ ) { + free( retv[i] ); + } + + free( retv ); + + free( path ); + + return pid; +} +static int sort_func ( const void *a, const void *b ) +{ + const char *astr = *( const char * const * )a; + const char *bstr = *( const char * const * )b; + return strcasecmp( astr,bstr ); +} +static char ** get_ssh ( ) +{ + int num_favorites = 0; + unsigned int index = 0; + char *path; + char **retv = NULL; +#ifdef TIMING + struct timespec start, stop; + clock_gettime( CLOCK_REALTIME, &start ); +#endif + + if ( getenv( "PATH" ) == NULL ) return NULL; + + const char *hd = getenv( "HOME" ); + + if ( hd == NULL ) return NULL; + + path = allocate( strlen( hd ) + strlen( "/.simpleswitcher.sshcache" )+2 ); + sprintf( path, "%s/%s", hd, ".simpleswitcher.sshcache" ); + FILE *fd = fopen ( path, "r" ); + char buffer[1024]; + + if ( fd != NULL ) { + while ( fgets( buffer,1024,fd ) != NULL ) { + retv = realloc( retv, ( index+2 )*sizeof( char* ) ); + buffer[strlen( buffer )-1] = '\0'; + retv[index] = strdup( buffer ); + retv[index+1] = NULL; + index++; + num_favorites++; + } + + fclose( fd ); + } + + free( path ); + + path = allocate( strlen( hd ) + strlen( "/.ssh/config" )+2 ); + sprintf( path, "%s/%s", hd, ".ssh/config" ); + fd = fopen ( path, "r" ); + + if ( fd != NULL ) { + while ( fgets( buffer,1024,fd ) != NULL ) { + if(strncasecmp(buffer, "Host", 4) == 0) { + int start = 0, stop=0; + buffer[strlen( buffer )-1] = '\0'; + for(start=4; isspace(buffer[start]);start++); + for(stop=start; isalnum(buffer[stop]);stop++); + retv = realloc( retv, ( index+2 )*sizeof( char* ) ); + retv[index] = strndup( &buffer[start], stop-start ); + retv[index+1] = NULL; + index++; + num_favorites++; + } + } + + fclose( fd ); + } + + // TODO: check this is still fast enough. (takes 1ms on laptop.) + qsort( &retv[num_favorites],index-num_favorites, sizeof( char* ), sort_func ); + free( path ); +#ifdef TIMING + clock_gettime( CLOCK_REALTIME, &stop ); + + if ( stop.tv_sec != start.tv_sec ) { + stop.tv_nsec += ( stop.tv_sec-start.tv_sec )*1e9; + } + + long diff = stop.tv_nsec-start.tv_nsec; + printf( "Time elapsed: %ld us\n", diff/1000 ); +#endif + return retv; +} + +static int token_match ( char **tokens, const char *input, + __attribute__( ( unused ) )int index, + __attribute__( ( unused ) )void *data) +{ + int match = 1; + // Do a tokenized match. + if ( tokens ) for ( int j = 1; match && tokens[j]; j++ ) { + match = ( strcasestr( input, tokens[j] ) != NULL ); + } + return match; +} + +SwitcherMode ssh_switcher_dialog ( char **input ) +{ + SwitcherMode retv = MODE_EXIT; + // act as a launcher + char **cmd_list = get_ssh( ); + + if ( cmd_list == NULL ) { + cmd_list = allocate( 2*sizeof( char * ) ); + cmd_list[0] = strdup( "No ssh hosts found" ); + cmd_list[1] = NULL; + } + + int shift=0; + int n = menu( cmd_list, input, "ssh ", 0, NULL, &shift,token_match, NULL); + + if ( n == -2 ) { + retv = WINDOW_SWITCHER; + } else if ( n >=0 && cmd_list[n] != NULL ) { + exec_ssh( cmd_list[n] ); + } else if ( n == -3 && *input != NULL && *input[0] != '\0' ) { + exec_ssh( *input ); + } + + for ( int i=0; cmd_list[i] != NULL; i++ ) { + free( cmd_list[i] ); + } + + free( cmd_list ); + + return retv; +}