File Coverage

bin/sshss
Criterion Covered Total %
statement 60 69 86.9
branch 19 20 95.0
condition 4 9 44.4
subroutine 13 14 92.8
pod n/a
total 96 112 85.7


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3 5     5   3103 use 5.006;
  5         13  
4 5     5   23 use strict;
  5         6  
  5         92  
5 5     5   19 use warnings;
  5         7  
  5         192  
6              
7             our $VERSION = '0.003';
8              
9             package App::SSH::SwitchShell;
10              
11 5     5   21 use Cwd;
  5         11  
  5         243  
12 5     5   1298 use English qw(-no_match_vars);
  5         3439  
  5         22  
13 5     5   1306 use File::Spec;
  5         9  
  5         2532  
14              
15             sub _exec (&@);
16              
17             if ( !caller ) {
18             main();
19             exit 1;
20             }
21              
22             sub main {
23 7     7   21562 configure_home();
24 7         13 my $shell = configure_shell();
25              
26             # Get the last component of the shell name.
27 7         76 my $shell0 = ( File::Spec->splitpath($shell) )[2];
28              
29             # If we have no command, execute the shell. In this case, the shell
30             # name to be passed in argv[0] is preceded by '-' to indicate that
31             # this is a login shell.
32              
33 7 100       21 if ( !exists $ENV{SSH_ORIGINAL_COMMAND} ) {
34              
35             # Launch a login shell
36 6     6   55 _exec { $shell } "-$shell0";
  6         31408  
37             }
38             else {
39             # Execute the command using the user's shell. This uses the -c
40             # option to execute the command.
41 1     1   10 _exec { $shell } $shell0, '-c', $ENV{SSH_ORIGINAL_COMMAND};
  1         5421  
42             }
43              
44 7         43 return;
45             }
46              
47             # We have to run exec from a small wrapper function to be able to test this
48             # script
49             # https://stackoverflow.com/questions/44597021/how-do-you-test-exec-used-with-indirect-object-syntax
50             sub _exec (&@) {
51 0     0   0 my ( $file, @argv ) = @_;
52 0         0 $file = $file->();
53              
54             {
55 0         0 exec {$file} @argv;
  0         0  
  0         0  
56             }
57              
58 0         0 print {*STDERR} "$ERRNO: $file\n";
  0         0  
59 0         0 exit 1;
60             }
61              
62             # Update the HOME env variable and change to the new home directory if we
63             # are configured for a shared account. Otherwise do nothung because SSH
64             # already ensures that HOME is configured correctly and we are chdir'd
65             # into it.
66             sub configure_home {
67 11     11   24204 my $myhome = get_abs_script_basedir();
68              
69 11         111 my @dirs = File::Spec->splitdir($myhome);
70 11 100       36 return if $dirs[-1] ne '.ssh';
71 10         16 pop @dirs;
72 10         68 $myhome = File::Spec->catdir(@dirs);
73              
74 10 100 33     196 if ( exists $ENV{HOME} && defined $ENV{HOME} && -d $ENV{HOME} ) {
      66        
75 5         28 my $home = File::Spec->canonpath( $ENV{HOME} );
76              
77 5         172 my $home_rp = Cwd::realpath($home);
78 5         135 my $myhome_rp = Cwd::realpath($myhome);
79 5 100       21 return if $home_rp eq $myhome_rp;
80             }
81              
82 8         33 $ENV{HOME} = $myhome;
83              
84 8 100       67 if ( !chdir $myhome ) {
85 2         5 print {*STDERR} "Could not chdir to home '$myhome': $ERRNO";
  2         97  
86             }
87              
88 8         24 return;
89             }
90              
91             sub get_abs_script_basedir {
92 1     1   2928 my $basedir = File::Spec->rel2abs(__FILE__);
93 1         32 $basedir = ( File::Spec->splitpath($basedir) )[1];
94 1         10 $basedir = File::Spec->canonpath($basedir);
95              
96 1         10 return $basedir;
97             }
98              
99             sub configure_shell {
100 14     14   28386 my $shell = get_shell();
101              
102             # Make sure SHELL points to the correct shell, either the shell
103             # specified as argument, the shell from the password file, or /bin/sh
104 14         68 $ENV{SHELL} = $shell;
105              
106 14         27 return $shell;
107             }
108              
109             sub get_shell {
110              
111             # The shell can be specified as argument
112 14 100   14   30 if (@ARGV) {
113 13         25 my $shell = shift @ARGV;
114              
115 13 100       199 if ( !File::Spec->file_name_is_absolute($shell) ) {
    100          
116 2         5 print {*STDERR} "Shell '$shell' is not an absolute path\n";
  2         55  
117             }
118             elsif ( !-e $shell ) {
119 1         2 print {*STDERR} "Shell '$shell' does not exist\n";
  1         43  
120             }
121             else {
122 10 100       96 return $shell if -x $shell;
123              
124 2         3 print {*STDERR} "Shell '$shell' is not executable\n";
  2         42  
125             }
126             }
127              
128             # Get the shell from the password data. An empty shell field is
129             # legal, and means /bin/sh.
130              
131 6         24 my $shell = ( getpwuid $EUID )[8];
132 6 50 33     52 return $shell if defined $shell && $shell ne q{};
133 0           return '/bin/sh';
134             }
135              
136             1;
137              
138             __END__