version 1.1.1.1, 2012/02/17 15:09:30
|
version 1.1.1.3, 2021/03/17 00:32:36
|
Line 1
|
Line 1
|
#!/bin/sh | #!/usr/bin/env perl |
# This script can be used as a "remote shell" command that is only | # This is a "local shell" command that works like a remote shell but only for |
# capable of pretending to connect to "localhost". This is useful | # the local host. See the usage message for more details. |
# for testing or for running a local copy where the sender and the | |
# receiver needs to use different options (e.g. --fake-super). If | |
# we get a -l USER option, we try to use "sudo -u USER" to run the | |
# command. | |
|
|
user='' | use strict; |
do_cd=y # Default path is user's home dir, just like ssh. | use warnings; |
| use Getopt::Long; |
| use English '-no_match_vars'; |
|
|
while : ; do | &Getopt::Long::Configure('bundling'); |
case "$1" in | &Getopt::Long::Configure('require_order'); |
-l) user="$2"; shift; shift ;; | GetOptions( |
-l*) user=`echo "$1" | sed 's/^-l//'`; shift ;; | 'l=s' => \( my $login_name ), |
--no-cd) do_cd=n; shift ;; | '1|2|4|6|A|a|C|f|g|k|M|N|n|q|s|T|t|V|v|X|x|Y' => sub { }, # Ignore |
-*) shift ;; | 'b|c|D|e|F|i|L|m|O|o|p|R|S|w=s' => sub { }, # Ignore |
localhost) shift; break ;; | 'no-cd' => \( my $no_chdir ), |
*) echo "lsh: unable to connect to host $1" 1>&2; exit 1 ;; | 'sudo' => \( my $use_sudo ), |
esac | ) or &usage; |
done | &usage unless @ARGV > 1; |
|
|
if [ "$user" ]; then | my $host = shift; |
prefix='' | if ($host =~ s/^([^@]+)\@//) { |
if [ $do_cd = y ]; then | $login_name = $1; |
home=`perl -e "print((getpwnam('$user'))[7])"` | } |
prefix="cd '$home' ;" | if ($host eq 'lh') { |
fi | $no_chdir = 1; |
sudo -H -u "$user" sh -c "$prefix $*" | } elsif ($host ne 'localhost') { |
else | die "lsh: unable to connect to host $host\n"; |
[ $do_cd = y ] && cd | } |
eval "${@}" | |
fi | my ($home_dir, @cmd); |
| if ($login_name) { |
| my ($uid, $gid); |
| if ($login_name =~ /\D/) { |
| $uid = getpwnam($login_name); |
| die "Unknown user: $login_name\n" unless defined $uid; |
| } else { |
| $uid = $login_name; |
| } |
| ($login_name, $gid, $home_dir) = (getpwuid($uid))[0,3,7]; |
| if ($use_sudo) { |
| unshift @ARGV, "cd '$home_dir' &&" unless $no_chdir; |
| unshift @cmd, qw( sudo -H -u ), $login_name; |
| $no_chdir = 1; |
| } else { |
| my $groups = "$gid $gid"; |
| while (my ($grgid, $grmembers) = (getgrent)[2,3]) { |
| if ($grgid != $gid && $grmembers =~ /(^|\s)\Q$login_name\E(\s|$)/o) { |
| $groups .= " $grgid"; |
| } |
| } |
| |
| my ($ruid, $euid) = ($UID, $EUID); |
| $GID = $EGID = $groups; |
| $UID = $EUID = $uid; |
| die "Cannot set ruid: $! (use --sudo?)\n" if $UID == $ruid && $ruid != $uid; |
| die "Cannot set euid: $! (use --sudo?)\n" if $EUID == $euid && $euid != $uid; |
| |
| $ENV{USER} = $ENV{USERNAME} = $login_name; |
| $ENV{HOME} = $home_dir; |
| } |
| } else { |
| $home_dir = (getpwuid($UID))[7]; |
| } |
| |
| unless ($no_chdir) { |
| chdir $home_dir or die "Unable to chdir to $home_dir: $!\n"; |
| } |
| |
| push @cmd, '/bin/sh', '-c', "@ARGV"; |
| exec @cmd; |
| die "Failed to exec: $!\n"; |
| |
| sub usage |
| { |
| die <<EOT; |
| Usage: lsh [-l USER] [--sudo] [--no-cd] localhost COMMAND [...] |
| |
| This is a "local shell" command that works like a remote shell but only for the |
| local host. This is useful for rsync testing or for running a local copy where |
| the sender and the receiver need to use different options (e.g. --fake-super). |
| If the -l option is used, we try to become the USER, either directly (when |
| root) or by using "sudo -H -u USER" (requires --sudo option). |
| |
| Note that if you pass hostname "lh" instead of "localhost" that the --no-cd |
| option is implied. The default is to "cd \$HOME" to simulate ssh behavior. |
| EOT |
| } |