aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sshexec.190
-rw-r--r--sshexec.c59
2 files changed, 143 insertions, 6 deletions
diff --git a/sshexec.1 b/sshexec.1
index b734c5b..0dfe121 100644
--- a/sshexec.1
+++ b/sshexec.1
@@ -6,8 +6,9 @@ sshexec - run a command through ssh(1) with normal command syntax
.SH SYNOPSIS
.B sshexec
.RB [ {
-.RI [\fBssh= ssh-command ]
-.RI [\fBdir= directory ]
+.RI [\fBssh=\fP ssh-command ]
+.RI [\fBdir=\fP directory ]
+.RB [[\fIfd\fP]{ > , >> , >| , < , <> }[ & ] = \fIfile\fP]
.BR } ]
[ssh-option] ...\,
.I destination
@@ -97,6 +98,91 @@ In the remote, change working directory to
.I directory
before executing
.IR command .
+.TP
+.IB \fR[\fPfd\fP]\fP >= file
+After changing working directory (assuming one is specified),
+create or truncate the specified
+.I file
+and open it for writing, using file descriptor number
+.IR fd .
+(Default
+.I fd
+is 1 (standard output).)
+.TP
+.IB \fR[\fPfd\fP]\fP >>= file
+After changing working directory (assuming one is specified),
+create the specified
+.I file
+if it does not exist and open it for writing in
+append-mode, using file descriptor number
+.IR fd .
+(Default
+.I fd
+is 1 (standard output).)
+.TP
+.IB \fR[\fPfd\fP]\fP >|= file
+After changing working directory (assuming one is specified),
+create the specified
+.IR file ,
+but fail if it already exists, and open it for writing,
+using file descriptor number
+.IR fd .
+(Default
+.I fd
+is 1 (standard output).)
+.TP
+.IB \fR[\fPfd\fP]\fP <= file
+After changing working directory (assuming one is specified),
+open the specified
+.IR file ,
+for reading, using file descriptor number
+.IR fd .
+(Default
+.I fd
+is 0 (standard input).)
+.TP
+.IB \fR[\fPfd\fP]\fP <>= file
+After changing working directory (assuming one is specified),
+open the specified
+.IR file ,
+for reading and writing, creating it if it does not
+already exist, using file descriptor number
+.IR fd .
+(Default
+.I fd
+is 0 (standard input).)
+.TP
+.IB \fR[\fPfd\fP]\fP \fR{\fP>\fP,\fP>>\fP,\fP>|\fP}\fP&= file
+Duplicate the file descriptor
+.I fd
+giving the new file descriptor the number
+.IR file .
+(Default
+.I fd
+is 1 (standard output).)
+.TP
+.IB \fR[\fPfd\fP]\fP \fR{\fP>\fP,\fP>>\fP,\fP>|\fP}\fP&=-
+Close the file descriptor
+.IR fd .
+(Default
+.I fd
+is 1 (standard output).)
+.TP
+.IB \fR[\fPfd\fP]\fP \fR{\fP<\fP,\fP<>\fP}\fP&= file
+Duplicate the file descriptor
+.I fd
+giving the new file descriptor the number
+.IR file .
+(Default
+.I fd
+is 0 (standard input).)
+.TP
+.IB \fR[\fPfd\fP]\fP \fR{\fP<\fP,\fP<>\fP}\fP&=-
+Close the file descriptor
+.IR fd .
+(Default
+.I fd
+is 0 (standard input).)
.SH OPERANDS
The following operands are supported:
diff --git a/sshexec.c b/sshexec.c
index f8eaed6..1ad2cf8 100644
--- a/sshexec.c
+++ b/sshexec.c
@@ -19,9 +19,16 @@ static void
usage(void)
{
exitf("usage: %s [{ %s }] [ssh-option] ... destination command [argument] ...\n",
- argv0, "[ssh=command] [dir=directory]");
+ argv0, "[ssh=command] [dir=directory] [[fd]{>,>>,>|,<>}[&]=file]");
}
+
+struct redirection {
+ const char *asis;
+ const char *escape;
+};
+
+
static const enum optclass {
NO_RECOGNISED = 0,
NO_ARGUMENT,
@@ -54,7 +61,7 @@ static size_t command_len = 0;
} while (0)
#define build_command_asis(S)\
- build_command(S, sizeof(S) - 1)
+ build_command(S, strlen(S))
#define finalise_command()\
build_command("", 1)
@@ -122,8 +129,10 @@ main(int argc_unused, char *argv[])
{
const char *dir = NULL;
const char *ssh = NULL;
+ struct redirection *redirections = NULL;
+ size_t nredirections = 0;
const char *destination;
- char **opts;
+ char **opts, *p;
size_t nopts;
enum optclass class;
const char *arg;
@@ -142,17 +151,51 @@ main(int argc_unused, char *argv[])
usage();\
continue;\
}
+
if (*argv && !strcmp(*argv, "{")) {
argv++;
for (; *argv && strcmp(*argv, "}"); argv++) {
STORE_OPT(&ssh, "ssh")
STORE_OPT(&dir, "dir")
- usage();
+
+ p = *argv;
+ while (isdigit(*p))
+ p++;
+ if (p[0] == '>')
+ p = &p[1 + (p[1] == '>' || p[1] == '|')];
+ else if (p[0] == '<')
+ p = &p[1 + (p[1] == '>')];
+ else
+ usage();
+ if (p[p[0] == '&'] != '=')
+ usage();
+ redirections = realloc(redirections, (nredirections + 1U) * sizeof(*redirections));
+ if (!redirections)
+ exitf("%s: could not allocate enough memory\n", argv0);
+ if (*p == '&') {
+ p = &p[1];
+ memmove(p, &p[1], strlen(&p[1]) + 1U);
+ if (isdigit(*p)) {
+ p++;
+ while (isdigit(*p))
+ p++;
+ } else if (*p == '-') {
+ p++;
+ }
+ if (*p)
+ usage();
+ redirections[nredirections].escape = NULL;
+ } else {
+ *p++ = '\0';
+ redirections[nredirections].escape = p;
+ }
+ redirections[nredirections++].asis = *argv;
}
if (!*argv)
usage();
argv++;
}
+
#undef STORE_OPT
if (!ssh)
@@ -202,6 +245,14 @@ main(int argc_unused, char *argv[])
build_command_asis(" ");
build_command_escape(*argv);
}
+ for (i = 0; i < nredirections; i++) {
+ build_command_asis(" ");
+ build_command_asis(redirections[i].asis);
+ if (redirections[i].escape) {
+ build_command_asis(" ");
+ build_command_escape(redirections[i].escape);
+ }
+ }
finalise_command();
i = 0;