|
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 |
| | } |