1
0
Fork 0
mirror of https://github.com/davatorium/rofi.git synced 2024-11-25 13:55:34 -05:00

[SSH] Add support for parsing port number from known hosts file.

* Store port number in history.
* Parse the [host]:port format.
* Update default ssh command to (optionally) add -p {port}.

Fixes: #580
This commit is contained in:
Dave Davenport 2019-02-10 13:55:09 +01:00
parent 9af5cac65b
commit f879062417
3 changed files with 98 additions and 77 deletions

View file

@ -58,7 +58,7 @@ Settings config = {
.terminal_emulator = "rofi-sensible-terminal", .terminal_emulator = "rofi-sensible-terminal",
.ssh_client = "ssh", .ssh_client = "ssh",
/** Command when executing ssh. */ /** Command when executing ssh. */
.ssh_command = "{terminal} -e {ssh-client} {host}", .ssh_command = "{terminal} -e {ssh-client} {host} [-p {port}]",
/** Command when running */ /** Command when running */
.run_command = "{cmd}", .run_command = "{cmd}",
/** Command used to list executable commands. empty -> internal */ /** Command used to list executable commands. empty -> internal */

View file

@ -67,6 +67,12 @@
*/ */
#define SSH_TOKEN_DELIM "= \t\r\n" #define SSH_TOKEN_DELIM "= \t\r\n"
typedef struct _SshEntry {
char *hostname;
int port;
} SshEntry;
/** /**
* @param host The host to connect too * @param host The host to connect too
* *
@ -74,24 +80,31 @@
* *
* @returns FALSE On failure, TRUE on success * @returns FALSE On failure, TRUE on success
*/ */
static int execshssh ( const char *host ) static int execshssh ( const SshEntry *entry)
{ {
char **args = NULL; char **args = NULL;
int argsv = 0; int argsv = 0;
gchar *portstr = NULL;
if ( entry->port > 0 ) {
portstr = g_strdup_printf("%d", entry->port);
}
helper_parse_setup ( config.ssh_command, &args, &argsv,
"{host}", entry->hostname,
"{port}", portstr,
(char *) 0 );
g_free ( portstr );
helper_parse_setup ( config.ssh_command, &args, &argsv, "{host}", host, (char *) 0 ); gsize l = strlen ( "Connecting to '' via rofi" ) + strlen ( entry->hostname ) + 1;
gsize l = strlen ( "Connecting to '' via rofi" ) + strlen ( host ) + 1;
gchar *desc = g_newa ( gchar, l ); gchar *desc = g_newa ( gchar, l );
g_snprintf ( desc, l, "Connecting to '%s' via rofi", host ); g_snprintf ( desc, l, "Connecting to '%s' via rofi", entry->hostname );
RofiHelperExecuteContext context = { RofiHelperExecuteContext context = {
.name = "ssh", .name = "ssh",
.description = desc, .description = desc,
.command = "ssh", .command = "ssh",
}; };
return helper_execute ( NULL, args, "ssh ", host, &context ); return helper_execute ( NULL, args, "ssh ", entry->hostname, &context );
} }
/** /**
@ -99,20 +112,27 @@ static int execshssh ( const char *host )
* *
* SSH into the selected host, if successful update history. * SSH into the selected host, if successful update history.
*/ */
static void exec_ssh ( const char *host ) static void exec_ssh ( const SshEntry *entry )
{ {
if ( !host || !host[0] ) { if ( !(entry->hostname )|| !(entry->hostname[0]) ) {
return; return;
} }
if ( !execshssh ( host ) ) { if ( !execshssh ( entry ) ) {
return; return;
} }
// This happens in non-critical time (After launching app) // This happens in non-critical time (After launching app)
// It is allowed to be a bit slower. // It is allowed to be a bit slower.
char *path = g_build_filename ( cache_dir, SSH_CACHE_FILE, NULL ); char *path = g_build_filename ( cache_dir, SSH_CACHE_FILE, NULL );
history_set ( path, host ); // TODO update.
if ( entry->port > 0 ) {
char *store = g_strdup_printf("%s:%d", entry->hostname, entry->port );
history_set ( path, store );
g_free ( store );
} else {
history_set ( path, entry->hostname );
}
g_free ( path ); g_free ( path );
} }
@ -139,7 +159,7 @@ static void delete_ssh ( const char *host )
* *
* @returns updated list of hosts. * @returns updated list of hosts.
*/ */
static char **read_known_hosts_file ( char ** retv, unsigned int *length ) static SshEntry *read_known_hosts_file ( SshEntry * retv, unsigned int *length )
{ {
char *path = g_build_filename ( g_get_home_dir (), ".ssh", "known_hosts", NULL ); char *path = g_build_filename ( g_get_home_dir (), ".ssh", "known_hosts", NULL );
FILE *fd = fopen ( path, "r" ); FILE *fd = fopen ( path, "r" );
@ -160,10 +180,6 @@ static char **read_known_hosts_file ( char ** retv, unsigned int *length )
// Skip hashed hostnames. // Skip hashed hostnames.
continue; continue;
} }
if ( *start == '[' ) {
// Don't support port versions yet, TODO
continue;
}
// Find end of hostname set. // Find end of hostname set.
char *end = strstr ( start, " " ); char *end = strstr ( start, " " );
if ( end == NULL ) { if ( end == NULL ) {
@ -175,11 +191,20 @@ static char **read_known_hosts_file ( char ** retv, unsigned int *length )
start = strsep(&sep,", " ); start = strsep(&sep,", " );
while ( start ) while ( start )
{ {
int port = 0;
if ( start[0] == '[' ) {
start++;
char *end = strchr ( start, ']');
if ( end[1] == ':' ){
*end = '\0';
port = atoi ( &(end[2]) );
}
}
// Is this host name already in the list? // Is this host name already in the list?
// We often get duplicates in hosts file, so lets check this. // We often get duplicates in hosts file, so lets check this.
int found = 0; int found = 0;
for ( unsigned int j = 0; j < ( *length ); j++ ) { for ( unsigned int j = 0; j < ( *length ); j++ ) {
if ( !g_ascii_strcasecmp ( start, retv[j] ) ) { if ( !g_ascii_strcasecmp ( start, retv[j].hostname ) ) {
found = 1; found = 1;
break; break;
} }
@ -187,9 +212,11 @@ static char **read_known_hosts_file ( char ** retv, unsigned int *length )
if ( !found ) { if ( !found ) {
// Add this host name to the list. // Add this host name to the list.
retv = g_realloc ( retv, ( ( *length ) + 2 ) * sizeof ( char* ) ); retv = g_realloc ( retv, ( ( *length ) + 2 ) * sizeof ( SshEntry ) );
retv[( *length )] = g_strdup ( start ); retv[( *length )].hostname = g_strdup ( start );
retv[( *length ) + 1] = NULL; retv[( *length )].port = port;
retv[( *length ) + 1].hostname = NULL;
retv[( *length ) + 1].port = 0;
( *length )++; ( *length )++;
} }
start = strsep(&sep,", " ); start = strsep(&sep,", " );
@ -215,7 +242,7 @@ static char **read_known_hosts_file ( char ** retv, unsigned int *length )
* *
* @returns an updated list with the added hosts. * @returns an updated list with the added hosts.
*/ */
static char **read_hosts_file ( char ** retv, unsigned int *length ) static SshEntry *read_hosts_file ( SshEntry * retv, unsigned int *length )
{ {
// Read the hosts file. // Read the hosts file.
FILE *fd = fopen ( "/etc/hosts", "r" ); FILE *fd = fopen ( "/etc/hosts", "r" );
@ -243,7 +270,7 @@ static char **read_hosts_file ( char ** retv, unsigned int *length )
// We often get duplicates in hosts file, so lets check this. // We often get duplicates in hosts file, so lets check this.
int found = 0; int found = 0;
for ( unsigned int j = 0; j < ( *length ); j++ ) { for ( unsigned int j = 0; j < ( *length ); j++ ) {
if ( !g_ascii_strcasecmp ( token, retv[j] ) ) { if ( !g_ascii_strcasecmp ( token, retv[j].hostname ) ) {
found = 1; found = 1;
break; break;
} }
@ -253,8 +280,8 @@ static char **read_hosts_file ( char ** retv, unsigned int *length )
// Add this host name to the list. // Add this host name to the list.
retv = g_realloc ( retv, retv = g_realloc ( retv,
( ( *length ) + 2 ) * sizeof ( char* ) ); ( ( *length ) + 2 ) * sizeof ( char* ) );
retv[( *length )] = g_strdup ( token ); retv[( *length )].hostname = g_strdup ( token );
retv[( *length ) + 1] = NULL; retv[( *length ) + 1].hostname = NULL;
( *length )++; ( *length )++;
} }
} }
@ -281,7 +308,7 @@ static char **read_hosts_file ( char ** retv, unsigned int *length )
return retv; return retv;
} }
static void parse_ssh_config_file ( const char *filename, char ***retv, unsigned int *length, unsigned int num_favorites ) static void parse_ssh_config_file ( const char *filename, SshEntry **retv, unsigned int *length, unsigned int num_favorites )
{ {
FILE *fd = fopen ( filename, "r" ); FILE *fd = fopen ( filename, "r" );
@ -352,7 +379,7 @@ static void parse_ssh_config_file ( const char *filename, char ***retv, unsigned
// given num_favorites is max 25. // given num_favorites is max 25.
int found = 0; int found = 0;
for ( unsigned int j = 0; j < num_favorites; j++ ) { for ( unsigned int j = 0; j < num_favorites; j++ ) {
if ( !g_ascii_strcasecmp ( token, ( *retv )[j] ) ) { if ( !g_ascii_strcasecmp ( token, ( *retv )[j].hostname ) ) {
found = 1; found = 1;
break; break;
} }
@ -363,9 +390,9 @@ static void parse_ssh_config_file ( const char *filename, char ***retv, unsigned
} }
// Add this host name to the list. // Add this host name to the list.
( *retv ) = g_realloc ( ( *retv ), ( ( *length ) + 2 ) * sizeof ( char* ) ); ( *retv ) = g_realloc ( ( *retv ), ( ( *length ) + 2 ) * sizeof ( SshEntry ) );
( *retv )[( *length )] = g_strdup ( token ); ( *retv )[( *length )].hostname = g_strdup ( token );
( *retv )[( *length ) + 1] = NULL; ( *retv )[( *length ) + 1].hostname = NULL;
( *length )++; ( *length )++;
} }
} }
@ -387,9 +414,9 @@ static void parse_ssh_config_file ( const char *filename, char ***retv, unsigned
* *
* @return an array of strings containing all the hosts. * @return an array of strings containing all the hosts.
*/ */
static char ** get_ssh ( unsigned int *length ) static SshEntry * get_ssh ( unsigned int *length )
{ {
char **retv = NULL; SshEntry *retv = NULL;
unsigned int num_favorites = 0; unsigned int num_favorites = 0;
char *path; char *path;
@ -398,7 +425,21 @@ static char ** get_ssh ( unsigned int *length )
} }
path = g_build_filename ( cache_dir, SSH_CACHE_FILE, NULL ); path = g_build_filename ( cache_dir, SSH_CACHE_FILE, NULL );
retv = history_get_list ( path, length ); char **h = history_get_list ( path, length );
retv = malloc ( (*length)*sizeof(SshEntry));
for ( unsigned int i = 0; i < (*length); i++ ){
int port = 0;
char *portstr = strchr ( h[i], ':' );
if ( portstr != NULL ) {
*portstr = '\0';
port = atoi ( &(portstr[1]) );
}
retv[i].hostname = h[i];
retv[i].port = port;
}
g_free (h);
g_free ( path ); g_free ( path );
num_favorites = ( *length ); num_favorites = ( *length );
@ -424,7 +465,7 @@ static char ** get_ssh ( unsigned int *length )
typedef struct typedef struct
{ {
/** List if available ssh hosts.*/ /** List if available ssh hosts.*/
char **hosts_list; SshEntry *hosts_list;
/** Length of the #hosts_list.*/ /** Length of the #hosts_list.*/
unsigned int hosts_list_length; unsigned int hosts_list_length;
} SSHModePrivateData; } SSHModePrivateData;
@ -466,7 +507,10 @@ static void ssh_mode_destroy ( Mode *sw )
{ {
SSHModePrivateData *rmpd = (SSHModePrivateData *) mode_get_private_data ( sw ); SSHModePrivateData *rmpd = (SSHModePrivateData *) mode_get_private_data ( sw );
if ( rmpd != NULL ) { if ( rmpd != NULL ) {
g_strfreev ( rmpd->hosts_list ); for ( unsigned int i = 0; i < rmpd->hosts_list_length; i++ ){
g_free( rmpd->hosts_list[i].hostname );
}
g_free ( rmpd->hosts_list );
g_free ( rmpd ); g_free ( rmpd );
mode_set_private_data ( sw, NULL ); mode_set_private_data ( sw, NULL );
} }
@ -495,14 +539,15 @@ static ModeMode ssh_mode_result ( Mode *sw, int mretv, char **input, unsigned in
else if ( mretv & MENU_QUICK_SWITCH ) { else if ( mretv & MENU_QUICK_SWITCH ) {
retv = ( mretv & MENU_LOWER_MASK ); retv = ( mretv & MENU_LOWER_MASK );
} }
else if ( ( mretv & MENU_OK ) && rmpd->hosts_list[selected_line] != NULL ) { else if ( ( mretv & MENU_OK ) && rmpd->hosts_list[selected_line].hostname != NULL ) {
exec_ssh ( rmpd->hosts_list[selected_line] ); exec_ssh ( &(rmpd->hosts_list[selected_line]) );
} }
else if ( ( mretv & MENU_CUSTOM_INPUT ) && *input != NULL && *input[0] != '\0' ) { else if ( ( mretv & MENU_CUSTOM_INPUT ) && *input != NULL && *input[0] != '\0' ) {
exec_ssh ( *input ); SshEntry entry = { .hostname = *input, .port = 0 };
exec_ssh ( &entry );
} }
else if ( ( mretv & MENU_ENTRY_DELETE ) && rmpd->hosts_list[selected_line] ) { else if ( ( mretv & MENU_ENTRY_DELETE ) && rmpd->hosts_list[selected_line].hostname ) {
delete_ssh ( rmpd->hosts_list[selected_line] ); delete_ssh ( rmpd->hosts_list[selected_line].hostname );
// Stay // Stay
retv = RELOAD_DIALOG; retv = RELOAD_DIALOG;
ssh_mode_destroy ( sw ); ssh_mode_destroy ( sw );
@ -526,7 +571,7 @@ static ModeMode ssh_mode_result ( Mode *sw, int mretv, char **input, unsigned in
static char *_get_display_value ( const Mode *sw, unsigned int selected_line, G_GNUC_UNUSED int *state, G_GNUC_UNUSED GList **attr_list, int get_entry ) static char *_get_display_value ( const Mode *sw, unsigned int selected_line, G_GNUC_UNUSED int *state, G_GNUC_UNUSED GList **attr_list, int get_entry )
{ {
SSHModePrivateData *rmpd = (SSHModePrivateData *) mode_get_private_data ( sw ); SSHModePrivateData *rmpd = (SSHModePrivateData *) mode_get_private_data ( sw );
return get_entry ? g_strdup ( rmpd->hosts_list[selected_line] ) : NULL; return get_entry ? g_strdup ( rmpd->hosts_list[selected_line].hostname ) : NULL;
} }
/** /**
@ -541,7 +586,7 @@ static char *_get_display_value ( const Mode *sw, unsigned int selected_line, G_
static int ssh_token_match ( const Mode *sw, rofi_int_matcher **tokens, unsigned int index ) static int ssh_token_match ( const Mode *sw, rofi_int_matcher **tokens, unsigned int index )
{ {
SSHModePrivateData *rmpd = (SSHModePrivateData *) mode_get_private_data ( sw ); SSHModePrivateData *rmpd = (SSHModePrivateData *) mode_get_private_data ( sw );
return helper_token_match ( tokens, rmpd->hosts_list[index] ); return helper_token_match ( tokens, rmpd->hosts_list[index].hostname );
} }
#include "mode-private.h" #include "mode-private.h"
Mode ssh_mode = Mode ssh_mode =

View file

@ -71,39 +71,14 @@ static int stored_argc = 0;
/** copy of the argv pointer for use in the commandline argument parser */ /** copy of the argv pointer for use in the commandline argument parser */
static char **stored_argv = NULL; static char **stored_argv = NULL;
char *helper_string_replace_if_exists_v ( char * string, GHashTable *h );
void cmd_set_arguments ( int argc, char **argv ) void cmd_set_arguments ( int argc, char **argv )
{ {
stored_argc = argc; stored_argc = argc;
stored_argv = argv; stored_argv = argv;
} }
/**
* @param info To Match information on.
* @param res The string being generated.
* @param data User data
*
* Replace the entries. This function gets called by g_regex_replace_eval.
*
* @returns TRUE to stop replacement, FALSE to continue
*/
static gboolean helper_eval_cb ( const GMatchInfo *info, GString *res, gpointer data )
{
gchar *match;
// Get the match
match = g_match_info_fetch ( info, 0 );
if ( match != NULL ) {
// Lookup the match, so we can replace it.
gchar *r = g_hash_table_lookup ( (GHashTable *) data, match );
if ( r != NULL ) {
// Append the replacement to the string.
g_string_append ( res, r );
}
// Free match.
g_free ( match );
}
// Continue replacement.
return FALSE;
}
int helper_parse_setup ( char * string, char ***output, int *length, ... ) int helper_parse_setup ( char * string, char ***output, int *length, ... )
{ {
@ -129,11 +104,7 @@ int helper_parse_setup ( char * string, char ***output, int *length, ... )
} }
va_end ( ap ); va_end ( ap );
// Replace hits within {-\w+}. char *res = helper_string_replace_if_exists_v ( string, h );
GRegex *reg = g_regex_new ( "{[-\\w]+}", 0, 0, NULL );
char *res = g_regex_replace_eval ( reg, string, -1, 0, 0, helper_eval_cb, h, NULL );
// Free regex.
g_regex_unref ( reg );
// Destroy key-value storage. // Destroy key-value storage.
g_hash_table_destroy ( h ); g_hash_table_destroy ( h );
// Parse the string into shell arguments. // Parse the string into shell arguments.
@ -1283,12 +1254,11 @@ static gboolean helper_eval_cb2 ( const GMatchInfo *info, GString *res, gpointer
char *helper_string_replace_if_exists ( char * string, ... ) char *helper_string_replace_if_exists ( char * string, ... )
{ {
GError *error = NULL;
GHashTable *h; GHashTable *h;
h = g_hash_table_new ( g_str_hash, g_str_equal ); h = g_hash_table_new ( g_str_hash, g_str_equal );
// Add list from variable arguments.
va_list ap; va_list ap;
va_start ( ap, string ); va_start ( ap, string );
// Add list from variable arguments.
while ( 1 ) { while ( 1 ) {
char * key = va_arg ( ap, char * ); char * key = va_arg ( ap, char * );
if ( key == (char *) 0 ) { if ( key == (char *) 0 ) {
@ -1297,15 +1267,21 @@ char *helper_string_replace_if_exists ( char * string, ... )
char *value = va_arg ( ap, char * ); char *value = va_arg ( ap, char * );
g_hash_table_insert ( h, key, value ); g_hash_table_insert ( h, key, value );
} }
char *retv = helper_string_replace_if_exists_v ( string, h );
va_end ( ap ); va_end ( ap );
// Destroy key-value storage.
g_hash_table_destroy ( h );
return retv;
}
char *helper_string_replace_if_exists_v ( char * string, GHashTable *h )
{
GError *error = NULL;
// Replace hits within {-\w+}. // Replace hits within {-\w+}.
GRegex *reg = g_regex_new ( "\\[(.*)({[-\\w]+})(.*)\\]|({[\\w-]+})", 0, 0, NULL ); GRegex *reg = g_regex_new ( "\\[(.*)({[-\\w]+})(.*)\\]|({[\\w-]+})", 0, 0, NULL );
char *res = g_regex_replace_eval ( reg, string, -1, 0, 0, helper_eval_cb2, h, NULL ); char *res = g_regex_replace_eval ( reg, string, -1, 0, 0, helper_eval_cb2, h, NULL );
// Free regex. // Free regex.
g_regex_unref ( reg ); g_regex_unref ( reg );
// Destroy key-value storage.
g_hash_table_destroy ( h );
// Throw error if shell parsing fails. // Throw error if shell parsing fails.
if ( error ) { if ( error ) {
char *msg = g_strdup_printf ( "Failed to parse: '%s'\nError: '%s'", string, error->message ); char *msg = g_strdup_printf ( "Failed to parse: '%s'\nError: '%s'", string, error->message );