aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--LICENSE2
-rw-r--r--README72
-rw-r--r--sshcd.1147
-rw-r--r--sshexec.1145
-rw-r--r--sshexec.c158
5 files changed, 482 insertions, 42 deletions
diff --git a/LICENSE b/LICENSE
index 6ddac9a..73634bb 100644
--- a/LICENSE
+++ b/LICENSE
@@ -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
diff --git a/README b/README
index d7cbe87..8476cee 100644
--- a/README
+++ b/README
@@ -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
diff --git a/sshcd.1 b/sshcd.1
index ba08414..7b5ede4 100644
--- a/sshcd.1
+++ b/sshcd.1
@@ -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)
diff --git a/sshexec.1 b/sshexec.1
index d4ace81..9f37962 100644
--- a/sshexec.1
+++ b/sshexec.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)
diff --git a/sshexec.c b/sshexec.c
index e8089a6..7ed551a 100644
--- a/sshexec.c
+++ b/sshexec.c
@@ -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));