diff options
author | Mattias Andrée <m@maandree.se> | 2025-02-08 20:44:05 +0100 |
---|---|---|
committer | Mattias Andrée <m@maandree.se> | 2025-02-08 20:44:05 +0100 |
commit | 25fc15b25901188507ebca8548d4ddcdb7e05158 (patch) | |
tree | 22c8ad6ce61874227ab37302df2724d55f56ccef | |
parent | Fix LF support (diff) | |
download | sshexec-25fc15b25901188507ebca8548d4ddcdb7e05158.tar.gz sshexec-25fc15b25901188507ebca8548d4ddcdb7e05158.tar.bz2 sshexec-25fc15b25901188507ebca8548d4ddcdb7e05158.tar.xz |
Add environment variables that lets the user override the option classes of ssh
Signed-off-by: Mattias Andrée <m@maandree.se>
-rw-r--r-- | LICENSE | 2 | ||||
-rw-r--r-- | README | 72 | ||||
-rw-r--r-- | sshcd.1 | 147 | ||||
-rw-r--r-- | sshexec.1 | 145 | ||||
-rw-r--r-- | sshexec.c | 158 |
5 files changed, 482 insertions, 42 deletions
@@ -1,6 +1,6 @@ ISC License -© 2023 Mattias Andrée <m@maandree.se> +© 2023, 2024, 2025 Mattias Andrée <m@maandree.se> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -148,6 +148,78 @@ OPERANDS command must not contain an equals sign (=) or be just a dash ("-"). +ENVIRONMENT VARIABLES + The following environment variables affects the execution of + sshexec: + + PATH + Default. See to the Base Definitions volume of POSIX.1-2017, + Section 8.3, Other Environment Variables. This environment + variable affects where the sshexec utility can find the + ssh(1) utility or ssh-command. + + SSHEXEC_OPTS_NO_ARG + List of options that sshexec shall interpret as ssh(1) + options that do not have any argument. (Default is + 46AaCfGgKkMNnqsTtVvXxYy, meaning the options -4, -6, -A, + -a, -C, -f, -G, -g, -K, -k, -M, -N, -n, -q, -s, -T, -t, + -V, -v, -X, -x, -Y, and -y.) + + SSHEXEC_OPTS_ARG + List of options that sshexec shall interpret as ssh(1) + options that have an argument. (Default is + BbcDEeFIiJLlmOoPpQRSWw, meaning the options -B, -b, -c, + -D, -E, -e, -F, -I, -i, -J, -L, -l, -m, -O, -o, -P, -p, + -Q, -R, -S, -W, and -w.) + + SSHEXEC_OPTS_OPT_ATTACHED_ARG + List of options that sshexec shall interpret as ssh(1) + options that have an argument only if there are + additional characters after the option character in the + same command line argument. (Default is the empty + string, meaning no options.) + + SSHEXEC_OPTS_OPT_ARG + List of options that sshexec shall interpret as ssh(1) + options that have an argument if there are additional + characters after the option character in the same + command line argument or if argument is followed + directly by another argument which does not start with + a dash (-). (Default is the empty string, meaning no + options.) + + SSHEXEC_LONG_OPTS_NO_ARG + Space-separated list of long options that sshexec shall + interpret as ssh(1) options that do not have any + argument unless it is followed directly by an equals + sign (=) in the same command line argument. Options + that do not start with two dashes (--) are silently + ignored. (Default is the empty string, meaning no + options.) + + SSHEXEC_LONG_OPTS_ARG + Space-separated list of long options that sshexec shall + interpret as ssh(1) options that have an argument that + must either be specified in the next command line + argument or after an equals sign (=) the shall directly + follow the option string in the same command line + argument. Options that do not start with two dashes + (--) are silently ignored. (Default is the empty string, + meaning no options.) + + SSHEXEC_LONG_OPTS_OPT_ARG + Space-separated list of long options that sshexec shall + interpret as ssh(1) options that have an argument if + it is the option string is is directly followed by + equals sign (=) in the same command line argument or if + argument is followed directly by another argument which + does not start with a dash (-). Options that do not + start with two dashes (--) are silently ignored. + (Default is the empty string, meaning no options.) + + Other environment variables may affect the execution of the + ssh(1) utility. + BUGS The remote shell must be sufficiently similar to sh(1posix). Namely, it must support the cd builtin command and the commands @@ -123,7 +123,7 @@ None. The following environment variables affects the execution of .BR sshcd : .TP -.SH PATH +.I PATH Default. See to the Base Definitions volume of POSIX.1-2017, Section 8.3, Other Environment Variables. This environment variable affects where the .B sshcd @@ -132,13 +132,156 @@ utility can find the utility or .IR ssh-command . .TP -.B SSHCD_PTY_ALLOC_FLAG +.I SSHCD_PTY_ALLOC_FLAG Specifies the option to pass to .BR ssh (1) to tell SSH to allocate a pseudo terminal. If unset .B -t will be used. If set but empty, no flag will be passed to .BR ssh (1). +.TP +.I SSHEXEC_OPTS_NO_ARG +List of options that +.B sshexec +shall interpret as +.BR ssh (1) +options that do not have any argument. +(Default is +.BR 46AaCfGgKkMNnqsTtVvXxYy , +meaning the options +.BR -4 , +.BR -6 , +.BR -A , +.BR -a , +.BR -C , +.BR -f , +.BR -G , +.BR -g , +.BR -K , +.BR -k , +.BR -M , +.BR -N , +.BR -n , +.BR -q , +.BR -s , +.BR -T , +.BR -t , +.BR -V , +.BR -v , +.BR -X , +.BR -x , +.BR -Y , +and +.BR -y .) +.TP +.I SSHEXEC_OPTS_ARG +List of options that +.B sshexec +shall interpret as +.BR ssh (1) +options that have an argument. +(Default is +.BR BbcDEeFIiJLlmOoPpQRSWw , +meaning the options +.BR -B , +.BR -b , +.BR -c , +.BR -D , +.BR -E , +.BR -e , +.BR -F , +.BR -I , +.BR -i , +.BR -J , +.BR -L , +.BR -l , +.BR -m , +.BR -O , +.BR -o , +.BR -P , +.BR -p , +.BR -Q , +.BR -R , +.BR -S , +.BR -W , +and +.BR -w .) +.TP +.I SSHEXEC_OPTS_OPT_ATTACHED_ARG +List of options that +.B sshexec +shall interpret as +.BR ssh (1) +options that have an argument only if +there are additional characters after +the option character in the same +command line argument. (Default is +the empty string, meaning no options.) +.TP +.I SSHEXEC_OPTS_OPT_ARG +List of options that +.B sshexec +shall interpret as +.BR ssh (1) +options that have an argument if there +are additional characters after +the option character in the same +command line argument or if argument is +followed directly by another argument +which does not start with a dash +.RB ( - ). +(Default is +the empty string, meaning no options.) +.TP +.I SSHEXEC_LONG_OPTS_NO_ARG +Space-separated list of long options that +.B sshexec +shall interpret as +.BR ssh (1) +options that do not have any argument +unless it is followed directly by an +equals sign +.RB ( = ) +in the same command line argument. +Options that do not start with two dashes +.RB ( -- ) +are silently ignored. (Default is the +empty string, meaning no options.) +.TP +.I SSHEXEC_LONG_OPTS_ARG +Space-separated list of long options that +.B sshexec +shall interpret as +.BR ssh (1) +options that have an argument that must +either be specified in the next command +line argument or after an +equals sign +.RB ( = ) +the shall directly follow the option +string in the same command line argument. +Options that do not start with two dashes +.RB ( -- ) +are silently ignored. (Default is the +empty string, meaning no options.) +.TP +.I SSHEXEC_LONG_OPTS_OPT_ARG +Space-separated list of long options that +.B sshexec +shall interpret as +.BR ssh (1) +options that have an argument if it is +the option string is is directly followed +by equals sign +.RB ( = ) +in the same command line argument or if +argument is followed directly by another +argument which does not start with a dash +.RB ( - ). +Options that do not start with two dashes +.RB ( -- ) +are silently ignored. (Default is the +empty string, meaning no options.) .PP Other environment variables may affect the execution of the .BR ssh (1) @@ -287,7 +287,7 @@ None. The following environment variables affects the execution of .BR sshexec : .TP -.SH PATH +.I PATH Default. See to the Base Definitions volume of POSIX.1-2017, Section 8.3, Other Environment Variables. This environment variable affects where the .B sshexec @@ -295,6 +295,149 @@ utility can find the .BR ssh (1) utility or .IR ssh-command . +.TP +.I SSHEXEC_OPTS_NO_ARG +List of options that +.B sshexec +shall interpret as +.BR ssh (1) +options that do not have any argument. +(Default is +.BR 46AaCfGgKkMNnqsTtVvXxYy , +meaning the options +.BR -4 , +.BR -6 , +.BR -A , +.BR -a , +.BR -C , +.BR -f , +.BR -G , +.BR -g , +.BR -K , +.BR -k , +.BR -M , +.BR -N , +.BR -n , +.BR -q , +.BR -s , +.BR -T , +.BR -t , +.BR -V , +.BR -v , +.BR -X , +.BR -x , +.BR -Y , +and +.BR -y .) +.TP +.I SSHEXEC_OPTS_ARG +List of options that +.B sshexec +shall interpret as +.BR ssh (1) +options that have an argument. +(Default is +.BR BbcDEeFIiJLlmOoPpQRSWw , +meaning the options +.BR -B , +.BR -b , +.BR -c , +.BR -D , +.BR -E , +.BR -e , +.BR -F , +.BR -I , +.BR -i , +.BR -J , +.BR -L , +.BR -l , +.BR -m , +.BR -O , +.BR -o , +.BR -P , +.BR -p , +.BR -Q , +.BR -R , +.BR -S , +.BR -W , +and +.BR -w .) +.TP +.I SSHEXEC_OPTS_OPT_ATTACHED_ARG +List of options that +.B sshexec +shall interpret as +.BR ssh (1) +options that have an argument only if +there are additional characters after +the option character in the same +command line argument. (Default is +the empty string, meaning no options.) +.TP +.I SSHEXEC_OPTS_OPT_ARG +List of options that +.B sshexec +shall interpret as +.BR ssh (1) +options that have an argument if there +are additional characters after +the option character in the same +command line argument or if argument is +followed directly by another argument +which does not start with a dash +.RB ( - ). +(Default is +the empty string, meaning no options.) +.TP +.I SSHEXEC_LONG_OPTS_NO_ARG +Space-separated list of long options that +.B sshexec +shall interpret as +.BR ssh (1) +options that do not have any argument +unless it is followed directly by an +equals sign +.RB ( = ) +in the same command line argument. +Options that do not start with two dashes +.RB ( -- ) +are silently ignored. (Default is the +empty string, meaning no options.) +.TP +.I SSHEXEC_LONG_OPTS_ARG +Space-separated list of long options that +.B sshexec +shall interpret as +.BR ssh (1) +options that have an argument that must +either be specified in the next command +line argument or after an +equals sign +.RB ( = ) +the shall directly follow the option +string in the same command line argument. +Options that do not start with two dashes +.RB ( -- ) +are silently ignored. (Default is the +empty string, meaning no options.) +.TP +.I SSHEXEC_LONG_OPTS_OPT_ARG +Space-separated list of long options that +.B sshexec +shall interpret as +.BR ssh (1) +options that have an argument if it is +the option string is is directly followed +by equals sign +.RB ( = ) +in the same command line argument or if +argument is followed directly by another +argument which does not start with a dash +.RB ( - ). +Options that do not start with two dashes +.RB ( -- ) +are silently ignored. (Default is the +empty string, meaning no options.) .PP Other environment variables may affect the execution of the .BR ssh (1) @@ -60,26 +60,6 @@ struct redirection { }; -static const enum optclass { - NO_RECOGNISED = 0, - NO_ARGUMENT, - MANDATORY_ARGUMENT -} sshopts[(size_t)1 << CHAR_BIT] = { -#define X(F) [F] = NO_ARGUMENT - X('4'), X('6'), X('A'), X('a'), X('C'), X('f'), X('G'), - X('g'), X('K'), X('k'), X('M'), X('N'), X('n'), X('q'), - X('s'), X('T'), X('t'), X('V'), X('v'), X('X'), X('x'), - X('Y'), X('y'), -#undef X -#define X(F) [F] = MANDATORY_ARGUMENT - X('B'), X('b'), X('c'), X('D'), X('E'), X('e'), X('F'), - X('I'), X('i'), X('J'), X('L'), X('l'), X('m'), X('O'), - X('o'), X('P'), X('p'), X('Q'), X('R'), X('S'), X('W'), - X('w') -#undef X -}; - - /** * Command being constructor for ssh(1) */ @@ -375,6 +355,45 @@ extract_directory_from_destination(void) /** + * Get an environment variable or a default value + * + * @param var The name of the environment variable + * @param def The default value to return if the environment variable is not set + * @return The value of the environment variable, or the default value if unset + */ +static const char * +get(const char *var, const char *def) +{ + const char *ret = getenv(var); + return ret ? ret : def; +} + + +/** + * Check if a string appears as a word in another string + * + * @param set The string to search in; words are separated by spaces + * @param sought The string to search for + * @return 1 if the string is found, 0 otherwise + */ +#if defined(__GNUC__) +__attribute__((__pure__)) +#endif +static int +contains(const char *set, const char *sought) +{ + size_t m, n = strlen(sought); + const char *p, *q; + for (p = set; (q = strchr(p, ' ')); p = &q[1]) { + m = (size_t)(q - p); + if (m == n && !strncmp(p, sought, n)) + return 1; + } + return !strcmp(p, sought); +} + + +/** * Read and parse the sshexec options * * @param argv The arguments from the command line after @@ -495,42 +514,107 @@ end_of_options: * arguments, this includes all arguments the * return value offsets `argv` except the "--" * (if there is one), so this includes both - * the options themselves and their arguments + * the options themselves and their arguments, + * and joined options countas one * @return `argv` offset to skip pass any ssh(1) options, * and any "--" immediately after them */ static char ** parse_ssh_options(char *argv[], size_t *nopts_out) { - enum optclass class; + const char *unarged_opts; + const char *arged_opts; + const char *optatarged_opts; + const char *optarged_opts; + const char *unarged_longopts; + const char *arged_longopts; + const char *optarged_longopts; const char *arg; - char opt; size_t nopts = 0; + /* Get option syntax from the environment */ + unarged_opts = get("SSHEXEC_OPTS_NO_ARG", "46AaCfGgKkMNnqsTtVvXxYy"); + arged_opts = get("SSHEXEC_OPTS_ARG", "BbcDEeFIiJLlmOoPpQRSWw"); + optatarged_opts = get("SSHEXEC_OPTS_OPT_ATTACHED_ARG", ""); + optarged_opts = get("SSHEXEC_OPTS_OPT_ARG", ""); + unarged_longopts = get("SSHEXEC_LONG_OPTS_NO_ARG", ""); + arged_longopts = get("SSHEXEC_LONG_OPTS_ARG", ""); + optarged_longopts = get("SSHEXEC_LONG_OPTS_OPT_ARG", ""); + + /* Count option arguments from the command line */ while (*argv) { + /* Break at the first non-option */ if (!strcmp(*argv, "--")) { argv++; break; } else if ((*argv)[0] != '-' || !(*argv)[1]) { break; } - arg = &(*argv++)[1]; + + arg = *argv++; nopts++; - while (*arg) { - opt = *arg++; - class = sshopts[(unsigned char)opt]; - if (class == MANDATORY_ARGUMENT) { - if (*arg) { - break; - } else if (*argv) { + if (arg[1] == '-') { + /* Long option */ + if (strchr(arg, '=')) { + /* Option has attach argument */ + } else if (contains(unarged_longopts, arg)) { + /* Option cannot have an argument */ + } else if (contains(arged_longopts, arg)) { + /* Option has detached argument */ + if (!*argv) + exitf("%s: argument missing for option %s\n", argv0, arg); + argv++; + nopts++; + } else if (contains(optarged_longopts, arg)) { + /* Long option with either detached argument or no argument */ + if (*argv && **argv != '-') { + /* The option has a detached argument */ argv++; nopts++; + } + } else { + exitf("%s: unrecognised option %s\n", argv0, arg); + } + } else { + /* Short option */ + arg++; + while (*arg) { + if (strchr(unarged_opts, *arg)) { + /* Option cannot have an argument */ + } else if (strchr(arged_opts, *arg)) { + /* Option must have an argument */ + if (arg[1]) { + /* Argument is attached to option */ + break; + } else if (argv[1]) { + /* Argument is detached from option */ + argv++; + nopts++; + break; + } else { + exitf("%s: argument missing for option -%c\n", argv0, *arg); + } + } else if (strchr(optatarged_opts, *arg)) { + /* Option may have an attached argument, but it cannot have a detached argument */ break; + } else if (strchr(optarged_opts, *arg)) { + /* Option may have an attached or detached argument */ + if (arg[1]) { + /* Argument is attached to option */ + } else { + /* Either there is no argument, or it is detached from the option */ + if (argv[1] && argv[1][0] != '-') { + /* Argument exist and is detached. We assume that if the next + * argument in the command line is the option's argument unless + * it starts with '-'. */ + argv++; + nopts++; + break; + } + } } else { - exitf("%s: argument missing for option -%c\n", argv0, opt); + exitf("%s: unrecognised option -%c\n", argv0, *arg); } - } else if (class == NO_RECOGNISED) { - exitf("%s: unrecognised option -%c\n", argv0, opt); } } } @@ -582,10 +666,8 @@ main(int argc_unused, char *argv[]) exitf("%s: could not allocate enough memory\n", argv0); args[i++] = ssh; if (is_sshcd) { - const char *pty_alloc_flag = getenv("SSHCD_PTY_ALLOC_FLAG"); - if (!pty_alloc_flag) - args[i++] = "-t"; - else if (*pty_alloc_flag) + const char *pty_alloc_flag = get("SSHCD_PTY_ALLOC_FLAG", "-t"); + if (*pty_alloc_flag) args[i++] = pty_alloc_flag; } memcpy(&args[i], opts, nopts * sizeof(*opts)); |