support: added install_deps_ubuntu.sh
[rsync.git] / support / lsh
1 #!/usr/bin/env perl
2 # This is a "local shell" command that works like a remote shell but only for
3 # the local host.  See the usage message for more details.
4
5 use strict;
6 use warnings;
7 use Getopt::Long;
8 use English '-no_match_vars';
9
10 &Getopt::Long::Configure('bundling');
11 &Getopt::Long::Configure('require_order');
12 GetOptions(
13     'l=s' => \( my $login_name ),
14     '1|2|4|6|A|a|C|f|g|k|M|N|n|q|s|T|t|V|v|X|x|Y' => sub { }, # Ignore
15     'b|c|D|e|F|i|L|m|O|o|p|R|S|w=s' => sub { }, # Ignore
16     'no-cd' => \( my $no_chdir ),
17     'sudo' => \( my $use_sudo ),
18     'rrsync=s' => \( my $rrsync_dir ),
19     'rropts=s' => \( my $rrsync_opts ),
20 ) or &usage;
21 &usage unless @ARGV > 1;
22
23 my $host = shift;
24 if ($host =~ s/^([^@]+)\@//) {
25     $login_name = $1;
26 }
27 if ($host eq 'lh') {
28     $no_chdir = 1;
29 } elsif ($host ne 'localhost') {
30     die "lsh: unable to connect to host $host\n";
31 }
32
33 my ($home_dir, @cmd);
34 if ($login_name) {
35     my ($uid, $gid);
36     if ($login_name =~ /\D/) {
37         $uid = getpwnam($login_name);
38         die "Unknown user: $login_name\n" unless defined $uid;
39     } else {
40         $uid = $login_name;
41     }
42     ($login_name, $gid, $home_dir) = (getpwuid($uid))[0,3,7];
43     if ($use_sudo) {
44         unshift @ARGV, "cd '$home_dir' &&" unless $no_chdir;
45         unshift @cmd, qw( sudo -H -u ), $login_name;
46         $no_chdir = 1;
47     } else {
48         my $groups = "$gid $gid";
49         while (my ($grgid, $grmembers) = (getgrent)[2,3]) {
50             if ($grgid != $gid && $grmembers =~ /(^|\s)\Q$login_name\E(\s|$)/o) {
51                 $groups .= " $grgid";
52             }
53         }
54
55         my ($ruid, $euid) = ($UID, $EUID);
56         $GID = $EGID = $groups;
57         $UID = $EUID = $uid;
58         die "Cannot set ruid: $! (use --sudo?)\n" if $UID == $ruid && $ruid != $uid;
59         die "Cannot set euid: $! (use --sudo?)\n" if $EUID == $euid && $euid != $uid;
60
61         $ENV{USER} = $ENV{USERNAME} = $login_name;
62         $ENV{HOME} = $home_dir;
63     }
64 } else {
65     $home_dir = (getpwuid($UID))[7];
66 }
67
68 unless ($no_chdir) {
69     chdir $home_dir or die "Unable to chdir to $home_dir: $!\n";
70 }
71
72 if ($rrsync_dir) {
73     $ENV{SSH_ORIGINAL_COMMAND} = join(' ', @ARGV);
74     push @cmd, 'rrsync';
75     if ($rrsync_opts) {
76         foreach my $opt (split(/[ ,]+/, $rrsync_opts)) {
77             $opt = "-$opt" unless $opt =~ /^-/;
78             push @cmd, $opt;
79         }
80     }
81     push @cmd, $rrsync_dir;
82 } else {
83     push @cmd, '/bin/sh', '-c', "@ARGV";
84 }
85 exec @cmd;
86 die "Failed to exec: $!\n";
87
88 sub usage
89 {
90     die <<EOT;
91 Usage: lsh [OPTIONS] localhost|lh COMMAND [...]
92
93 This is a "local shell" command that works like a remote shell but only for the
94 local host.  This is useful for rsync testing or for running a local copy where
95 the sender and the receiver need to use different options (e.g. --fake-super).
96
97 Options:
98
99 -l USER        Choose the USER that lsh tries to become.
100 --no-cd        Skip the chdir \$HOME (the default with hostname "lh")
101 --sudo         Use sudo -H -l USER to become root or the specified USER.
102 --rrsync=DIR   Test rrsync restricted copying without using ssh.
103 --rropts=STR   The string "munge,no-del,no-lock" would pass 3 options to
104                rrsync (must be combined with --rrsync=DIR).
105
106 The script also ignores a bunch of single-letter ssh options.
107 EOT
108 }