diff options
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | sshcd.1 | 211 | ||||
-rw-r--r-- | sshexec.1 | 5 | ||||
-rw-r--r-- | sshexec.c | 49 |
5 files changed, 260 insertions, 13 deletions
@@ -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 @@ -158,4 +158,4 @@ BUGS characters. SEE ALSO - ssh(1) + ssh(1), 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 > @@ -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 @@ -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++] = "--"; |