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:
parent
9af5cac65b
commit
f879062417
3 changed files with 98 additions and 77 deletions
|
@ -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 */
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in a new issue