1
0
Fork 0
mirror of https://github.com/davatorium/rofi.git synced 2024-11-11 13:50:48 -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",
.ssh_client = "ssh",
/** Command when executing ssh. */
.ssh_command = "{terminal} -e {ssh-client} {host}",
.ssh_command = "{terminal} -e {ssh-client} {host} [-p {port}]",
/** Command when running */
.run_command = "{cmd}",
/** Command used to list executable commands. empty -> internal */

View file

@ -67,6 +67,12 @@
*/
#define SSH_TOKEN_DELIM "= \t\r\n"
typedef struct _SshEntry {
char *hostname;
int port;
} SshEntry;
/**
* @param host The host to connect too
*
@ -74,24 +80,31 @@
*
* @returns FALSE On failure, TRUE on success
*/
static int execshssh ( const char *host )
static int execshssh ( const SshEntry *entry)
{
char **args = NULL;
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 ( host ) + 1;
gsize l = strlen ( "Connecting to '' via rofi" ) + strlen ( entry->hostname ) + 1;
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 = {
.name = "ssh",
.description = desc,
.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.
*/
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;
}
if ( !execshssh ( host ) ) {
if ( !execshssh ( entry ) ) {
return;
}
// This happens in non-critical time (After launching app)
// It is allowed to be a bit slower.
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 );
}
@ -139,7 +159,7 @@ static void delete_ssh ( const char *host )
*
* @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 );
FILE *fd = fopen ( path, "r" );
@ -160,10 +180,6 @@ static char **read_known_hosts_file ( char ** retv, unsigned int *length )
// Skip hashed hostnames.
continue;
}
if ( *start == '[' ) {
// Don't support port versions yet, TODO
continue;
}
// Find end of hostname set.
char *end = strstr ( start, " " );
if ( end == NULL ) {
@ -175,11 +191,20 @@ static char **read_known_hosts_file ( char ** retv, unsigned int *length )
start = strsep(&sep,", " );
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?
// We often get duplicates in hosts file, so lets check this.
int found = 0;
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;
break;
}
@ -187,9 +212,11 @@ static char **read_known_hosts_file ( char ** retv, unsigned int *length )
if ( !found ) {
// Add this host name to the list.
retv = g_realloc ( retv, ( ( *length ) + 2 ) * sizeof ( char* ) );
retv[( *length )] = g_strdup ( start );
retv[( *length ) + 1] = NULL;
retv = g_realloc ( retv, ( ( *length ) + 2 ) * sizeof ( SshEntry ) );
retv[( *length )].hostname = g_strdup ( start );
retv[( *length )].port = port;
retv[( *length ) + 1].hostname = NULL;
retv[( *length ) + 1].port = 0;
( *length )++;
}
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.
*/
static char **read_hosts_file ( char ** retv, unsigned int *length )
static SshEntry *read_hosts_file ( SshEntry * retv, unsigned int *length )
{
// Read the hosts file.
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.
int found = 0;
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;
break;
}
@ -253,8 +280,8 @@ static char **read_hosts_file ( char ** retv, unsigned int *length )
// Add this host name to the list.
retv = g_realloc ( retv,
( ( *length ) + 2 ) * sizeof ( char* ) );
retv[( *length )] = g_strdup ( token );
retv[( *length ) + 1] = NULL;
retv[( *length )].hostname = g_strdup ( token );
retv[( *length ) + 1].hostname = NULL;
( *length )++;
}
}
@ -281,7 +308,7 @@ static char **read_hosts_file ( char ** retv, unsigned int *length )
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" );
@ -352,7 +379,7 @@ static void parse_ssh_config_file ( const char *filename, char ***retv, unsigned
// given num_favorites is max 25.
int found = 0;
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;
break;
}
@ -363,9 +390,9 @@ static void parse_ssh_config_file ( const char *filename, char ***retv, unsigned
}
// Add this host name to the list.
( *retv ) = g_realloc ( ( *retv ), ( ( *length ) + 2 ) * sizeof ( char* ) );
( *retv )[( *length )] = g_strdup ( token );
( *retv )[( *length ) + 1] = NULL;
( *retv ) = g_realloc ( ( *retv ), ( ( *length ) + 2 ) * sizeof ( SshEntry ) );
( *retv )[( *length )].hostname = g_strdup ( token );
( *retv )[( *length ) + 1].hostname = NULL;
( *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.
*/
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;
char *path;
@ -398,7 +425,21 @@ static char ** get_ssh ( unsigned int *length )
}
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 );
num_favorites = ( *length );
@ -424,7 +465,7 @@ static char ** get_ssh ( unsigned int *length )
typedef struct
{
/** List if available ssh hosts.*/
char **hosts_list;
SshEntry *hosts_list;
/** Length of the #hosts_list.*/
unsigned int hosts_list_length;
} SSHModePrivateData;
@ -466,7 +507,10 @@ static void ssh_mode_destroy ( Mode *sw )
{
SSHModePrivateData *rmpd = (SSHModePrivateData *) mode_get_private_data ( sw );
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 );
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 ) {
retv = ( mretv & MENU_LOWER_MASK );
}
else if ( ( mretv & MENU_OK ) && rmpd->hosts_list[selected_line] != NULL ) {
exec_ssh ( rmpd->hosts_list[selected_line] );
else if ( ( mretv & MENU_OK ) && rmpd->hosts_list[selected_line].hostname != NULL ) {
exec_ssh ( &(rmpd->hosts_list[selected_line]) );
}
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] ) {
delete_ssh ( rmpd->hosts_list[selected_line] );
else if ( ( mretv & MENU_ENTRY_DELETE ) && rmpd->hosts_list[selected_line].hostname ) {
delete_ssh ( rmpd->hosts_list[selected_line].hostname );
// Stay
retv = RELOAD_DIALOG;
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 )
{
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 )
{
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"
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 */
static char **stored_argv = NULL;
char *helper_string_replace_if_exists_v ( char * string, GHashTable *h );
void cmd_set_arguments ( int argc, char **argv )
{
stored_argc = argc;
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, ... )
{
@ -129,11 +104,7 @@ int helper_parse_setup ( char * string, char ***output, int *length, ... )
}
va_end ( ap );
// Replace hits within {-\w+}.
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 );
char *res = helper_string_replace_if_exists_v ( string, h );
// Destroy key-value storage.
g_hash_table_destroy ( h );
// 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, ... )
{
GError *error = NULL;
GHashTable *h;
h = g_hash_table_new ( g_str_hash, g_str_equal );
// Add list from variable arguments.
va_list ap;
va_start ( ap, string );
// Add list from variable arguments.
while ( 1 ) {
char * key = va_arg ( ap, char * );
if ( key == (char *) 0 ) {
@ -1297,15 +1267,21 @@ char *helper_string_replace_if_exists ( char * string, ... )
char *value = va_arg ( ap, char * );
g_hash_table_insert ( h, key, value );
}
char *retv = helper_string_replace_if_exists_v ( string, h );
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+}.
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 );
// Free regex.
g_regex_unref ( reg );
// Destroy key-value storage.
g_hash_table_destroy ( h );
// Throw error if shell parsing fails.
if ( error ) {
char *msg = g_strdup_printf ( "Failed to parse: '%s'\nError: '%s'", string, error->message );