aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2025-02-08 18:00:12 +0100
committerMattias Andrée <m@maandree.se>2025-02-08 18:00:12 +0100
commit9b5c5b77bd9939e944c736096701e3041f984af8 (patch)
tree7eb87d40c3f0b09d0364a63ea48a539c5aee8f34
parentAdd asis and nasis options (diff)
downloadsshexec-9b5c5b77bd9939e944c736096701e3041f984af8.tar.gz
sshexec-9b5c5b77bd9939e944c736096701e3041f984af8.tar.bz2
sshexec-9b5c5b77bd9939e944c736096701e3041f984af8.tar.xz
add sshcd + fix bug
Signed-off-by: Mattias Andrée <m@maandree.se>
-rw-r--r--Makefile6
-rw-r--r--README2
-rw-r--r--sshcd.1211
-rw-r--r--sshexec.15
-rw-r--r--sshexec.c49
5 files changed, 260 insertions, 13 deletions
diff --git a/Makefile b/Makefile
index 9dc18bf..9812e19 100644
--- a/Makefile
+++ b/Makefile
@@ -21,11 +21,17 @@ install: sshexec
mkdir -p -- "$(DESTDIR)$(PREFIX)/bin"
mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1/"
cp -- sshexec "$(DESTDIR)$(PREFIX)/bin/"
+ test ! -d "$(DESTDIR)$(PREFIX)/bin/sshcd"
+ ln -sf -- sshexec "$(DESTDIR)$(PREFIX)/bin/sshcd"
cp -- sshexec.1 "$(DESTDIR)$(MANPREFIX)/man1/"
+ cp -- sshcd "$(DESTDIR)$(PREFIX)/bin/"
+ cp -- sshcd.1 "$(DESTDIR)$(MANPREFIX)/man1/"
uninstall:
-rm -f -- "$(DESTDIR)$(PREFIX)/bin/sshexec"
-rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/sshexec.1"
+ -rm -f -- "$(DESTDIR)$(PREFIX)/bin/sshcd"
+ -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/sshcd.1"
clean:
-rm -f -- *.o *.a *.lo *.su *.so *.so.* *.gch *.gcov *.gcno *.gcda
diff --git a/README b/README
index b87c6d3..d7cbe87 100644
--- a/README
+++ b/README
@@ -158,4 +158,4 @@ BUGS
characters.
SEE ALSO
- ssh(1)
+ ssh(1), sshcd(1)
diff --git a/sshcd.1 b/sshcd.1
new file mode 100644
index 0000000..ba08414
--- /dev/null
+++ b/sshcd.1
@@ -0,0 +1,211 @@
+.TH SSHCD 1 sshexec
+
+.SH NAME
+sshcd - open ssh(1) with a specific remote working directory
+
+.SH SYNOPSIS
+.B sshcd
+.RB [ {
+.RI [\fBssh=\fP ssh-command ]
+.RB [ cd= ( strict | lax )]
+.BR } ]
+.RI [ ssh-option ]\ ...\,
+.I destination
+
+.SH DESCRIPTION
+The
+.B sshcd
+utility is a wrapper for SSH that makes it lets the user
+specify the directory remote working directory.
+.PP
+.B sshcd
+passes any argument after
+.B }
+to
+.I ssh-command
+.RB ( ssh
+if not specified), except it may rewrite
+.I destination
+to remove information that's not supported by
+.BR ssh (1).
+
+.SH OPTIONS
+.B sshcd
+options may be placed at the very beginning enclosed with
+the arguments
+.B {
+and
+.BR } .
+.B sshcd
+options, if any, shall be placed in the same
+.B {
+.BR } -group.
+Any other option will be passed as is to the
+.BR ssh (1)
+utility or
+.IR ssh-command .
+The
+.B sshcd
+utility has a build it list of options recognised by the
+.BR ssh (1)
+utility and will not allow anything matching this list.
+The
+.B sshcd
+utility does not allow mixing options and operands: no
+option may be placed after
+.IR destination .
+.PP
+The following
+.B sshcd
+options are supported:
+.TP
+.BI ssh= ssh-command
+Instead of looking for
+.B ssh
+in
+.IR PATH ,
+the
+.B sshcd
+utility shall use
+.IR ssh-command ,
+which it will look for in
+.I PATH
+if it is only a file name (does not contain a slash
+.RB ( / )).
+.TP
+.B cd=strict
+Fail without executing the
+.I command
+if it's not possible to set
+.I directory
+as the remote working directory.
+.TP
+.B cd=lax
+Continue (but warn) executing the
+.I command
+even if it's not possible to set
+.I directory
+as the remote working directory.
+
+.SH OPERANDS
+The following operands are supported:
+.TP
+.I destination
+The destination to connect and log into. It shall be either in
+the form
+.RI [ user\fP\fB@ ] hostname [\fB:\fP directory ]
+or in the form
+.BR ssh [ cd ] :// [\fIuser @ ]\fIhostname\fP[ : \fIport\fP][ / \fIdirectory\fP].
+
+.I user
+shall be the name of the remote user. If not specified,
+the name of the local user running the utility will be used.
+
+.I hostname
+shall be the address to the remote machine.
+
+.I port
+shall be the port or service name for the port to
+connect to on the remote machine.
+
+.I directory
+shall be directory to change the remote working directory.
+
+.SH STDIN
+The
+.B sshcd
+utility itself does not use the standard input.
+
+.SH INPUT FILES
+None.
+
+.SH ENVIRONMENT VARIABLES
+The following environment variables affects the execution of
+.BR sshcd :
+.TP
+.SH 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
+utility can find the
+.BR ssh (1)
+utility or
+.IR ssh-command .
+.TP
+.B 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).
+.PP
+Other environment variables may affect the execution of the
+.BR ssh (1)
+utility.
+
+.SH ASYNCHRONOUS EVENTS
+Default.
+
+.SH STDOUT
+The
+.B sshcd
+utility itself does not use the standard output.
+
+.SH STDERR
+The standard error is used for diagnostic messages in the
+.B sshcd
+utility itself.
+
+.SH OUTPUT FILES
+None.
+
+.SH EXTENDED DESCRIPTION
+None.
+
+.SH EXIT STATUS
+The
+.B sshcd
+utility exits with the exit status of the
+.BR ssh (1)
+utility or with 255 if an error occurred.
+
+.SH CONSEQUENCES OF ERRORS
+Default.
+
+.SH APPLICATION USAGE
+None.
+
+.SH EXAMPLES
+None.
+
+.SH RATIONALE
+For historical reasons, the
+.B sshcd
+utility does not let the user add a command to run
+inside the directory. This also avoids problems; if the
+user want to run a command in a specific directory,
+.BR sshexec (1)
+lets the user do so in an intuitive manner; for other
+traditional syntax,
+.BR ssh (1)
+can still be used — specifying a
+.BR cd (1)
+command at the beginning shouldn't be a problem.
+
+.SH NOTES
+None.
+
+.SH BUGS
+None.
+
+.SH FUTURE DIRECTIONS
+None.
+
+.SH SEE ALSO
+.BR ssh (1),
+.BR sshexec (1)
+
+.SH AUTHORS
+Mattias Andrée
+.RI < m@maandree.se >
diff --git a/sshexec.1 b/sshexec.1
index e824cb5..d4ace81 100644
--- a/sshexec.1
+++ b/sshexec.1
@@ -23,7 +23,7 @@ The
.B sshexec
utility is a wrapper for SSH that makes it easy to run commands
directly in the SSH command.
-
+.PP
.B sshexec
passes any argument after
.B }
@@ -369,7 +369,8 @@ as special characters.
None.
.SH SEE ALSO
-.BR ssh (1)
+.BR ssh (1),
+.BR sshcd (1)
.SH AUTHORS
Mattias Andrée
diff --git a/sshexec.c b/sshexec.c
index e0912c9..aca9ec9 100644
--- a/sshexec.c
+++ b/sshexec.c
@@ -10,6 +10,7 @@
static const char *argv0 = "sshexec";
+static int is_sshcd = 0;
#define exitf(...) (fprintf(stderr, __VA_ARGS__), exit(255))
@@ -18,9 +19,11 @@ static const char *argv0 = "sshexec";
static void
usage(void)
{
- exitf("usage: %s [{ %s }] [ssh-option] ... destination command [argument] ...\n",
- argv0, "[ssh=command] [dir=directory] [cd=(strict|lax)] [[fd]{>,>>,>|,<>}[&]=file] "
- "[asis=asis-marker [nasis=asis-count]]");
+ exitf("usage: %s [{ [ssh=command]%s [cd=(strict|lax)]%s }] [ssh-option] ... destination%s\n",
+ argv0,
+ is_sshcd ? "" : " [dir=directory]",
+ is_sshcd ? "" : " [[fd]{>,>>,>|,<>}[&]=file] [asis=asis-marker [nasis=asis-count]]",
+ is_sshcd ? "" : " command [argument] ...");
}
@@ -153,6 +156,9 @@ main(int argc_unused, char *argv[])
if (*argv)
argv0 = *argv++;
+ p = strrchr(argv0, '/');
+ is_sshcd = !strcmp(p ? &p[1] : argv0, "sshcd");
+
#define STORE_OPT(VARP, OPT)\
if (!strncmp(*argv, OPT"=", sizeof(OPT))) {\
if (*(VARP) || !*(*(VARP) = &(*argv)[sizeof(OPT)]))\
@@ -164,6 +170,11 @@ main(int argc_unused, char *argv[])
argv++;
for (; *argv && strcmp(*argv, "}"); argv++) {
STORE_OPT(&ssh, "ssh")
+ STORE_OPT(&cd, "cd")
+
+ if (is_sshcd)
+ usage();
+
STORE_OPT(&dir, "dir")
STORE_OPT(&asis, "asis")
STORE_OPT(&nasis_str, "nasis")
@@ -211,7 +222,9 @@ main(int argc_unused, char *argv[])
if (!ssh)
ssh = "ssh";
- if (!cd || !strcmp(cd, "strict"))
+ if (!cd)
+ strict_cd = !is_sshcd;
+ else if (!strcmp(cd, "strict"))
strict_cd = 1;
else if (!strcmp(cd, "lax"))
strict_cd = 0;
@@ -259,19 +272,24 @@ main(int argc_unused, char *argv[])
}
destination = *argv++;
- if (!destination || !*argv)
+ if (!destination || (is_sshcd ? !!*argv : !*argv))
usage();
- if (!strcmp(*argv, "-"))
+ if (!strcmp(destination, "-"))
exitf("%s: the command argument must not be \"-\"\n", argv0);
- else if (strchr(*argv, '='))
+ else if (strchr(destination, '='))
exitf("%s: the command argument must contain an \'=\'\n", argv0);
- if (!strncmp(destination, "sshexec://", sizeof("sshexec://") - 1U)) {
+ if (!is_sshcd && !strncmp(destination, "sshexec://", sizeof("sshexec://") - 1U)) {
memmove(&destination[sizeof("ssh") - 1U],
&destination[sizeof("sshexec") - 1U],
strlen(&destination[sizeof("sshexec") - 1U]) + 1U);
goto using_ssh_prefix;
+ } else if (is_sshcd && !strncmp(destination, "sshcd://", sizeof("sshcd://") - 1U)) {
+ memmove(&destination[sizeof("ssh") - 1U],
+ &destination[sizeof("sshcd") - 1U],
+ strlen(&destination[sizeof("sshcd") - 1U]) + 1U);
+ goto using_ssh_prefix;
} else if (!strncmp(destination, "ssh://", sizeof("ssh://") - 1U)) {
using_ssh_prefix:
dest_dir_delim = strchr(&destination[sizeof("ssh://")], '/');
@@ -284,13 +302,16 @@ main(int argc_unused, char *argv[])
*dest_dir_delim++ = '\0';
dir = dest_dir_delim;
}
-dest_dir_checked:
if (dir) {
build_command_asis("cd -- ");
build_command_escape(dir);
build_command_asis(strict_cd ? " && " : " ; ");
}
+ if (is_sshcd) {
+ build_command_asis("exec \"$SHELL\" -l");
+ goto command_constructed;
+ }
if (asis)
build_command_asis("( env --");
else
@@ -320,13 +341,21 @@ dest_dir_checked:
build_command_escape(redirections[i].escape);
}
}
+command_constructed:
finalise_command();
i = 0;
- args = calloc(5 + nopts, sizeof(*args));
+ args = calloc(5U + (size_t)is_sshcd + nopts, sizeof(*args));
if (!args)
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)
+ args[i++] = pty_alloc_flag;
+ }
memcpy(&args[i], opts, nopts * sizeof(*opts));
i += nopts;
args[i++] = "--";