1#!/usr/bin/perl
2use strict;
3use warnings;
4use Text::ParseWords qw(parse_line);
5use Getopt::Long qw(:config posix_default);
6
7# Note: this script can't deal with ; delimiters which are run into other arguments
8
9# Hack off the quotes :(  yuk
10# Needed to work around the shonkiness of the tokeniser
11sub hack_off_quotes {
12    return map { s/^'(.*)'$/$1/ or s/^"(.*)"$/$1/; $_ } @_
13}
14
15
16my $unauthenticated = 1;
17sub sudo {
18    my ($params, %env) = @_;
19
20    # Locally alias @ARGV to $params rather than use
21    # GetOptionsFromArray, for the sake of portability (Some currently
22    # widely used versions of Perl don't have a version of
23    # Getopt::Long with it).
24    local *ARGV = $params;
25
26    shift @$params;
27
28    my $pw_prompt = '[sudo] password for user: ';
29    my $unauthenticate = 0;
30
31    GetOptions(
32        "K" => \$unauthenticate,
33        "p=s" => \$pw_prompt,
34    )
35        or warn "oops, bad parameters to sudo\n";
36
37    if ($unauthenticate) {
38        $unauthenticated = 1;
39        # ignore anything else
40        return;
41    }
42
43    if ($unauthenticated) {
44        print hack_off_quotes $pw_prompt;
45        my $input = <STDIN>;
46#        print "\n";
47    }
48
49    $unauthenticated = 0;
50
51    if ($params->[0] eq 'sh') { # ignore the rest if any
52        return shell(%env);
53    }
54
55    print "sudo got: @$params\n";
56}
57
58
59sub shell {
60    my %env = @_;
61    $env{PS1} ||= 'sh$ ';
62
63    PROMPT: while (1) {
64        print $env{PS1};
65
66        my @cmds = [];
67
68        # Parse line, then split any parameters like
69        # "foo;" into "foo" and ";" (so that we can parse ; delimiters
70        # consistently).  This will have some edge cases like
71        #
72        #   foo bar="baz;" biz
73        #
74        # which gets parsed as two commands:
75        #
76        #   "foo", "bar=baz"
77        #   "biz"
78        #
79        # but we don't really care about them.
80
81        my $line = <STDIN>;
82        chomp $line;
83        my @toks = parse_line '\s+', 1, $line;
84        @toks = grep { defined $_ } @toks;
85        @toks = map { /(.*);$/? ($1, ';') : ($_) } @toks;
86        for my $tok (@toks) {
87            $tok eq ';'?
88                push @cmds, []  :
89                push @{ $cmds[-1] }, $tok;
90        }
91
92    CMD: for my $toks (@cmds) {
93            @$toks
94                or next;
95
96            local $_ = $toks->[0];
97
98            /^exit$/
99                and last PROMPT;
100
101            /^(\w+)=(.*)/
102                and do {
103                    $env{$1} = $2;
104                    next CMD;
105                };
106
107            /^export$/
108                and next CMD; # ignore it
109
110            /^sh$/
111                and do {
112                    shell(%env);
113                    next CMD;
114                };
115
116            /^sudo$/
117                and do {
118                    sudo($toks, %env);
119                    next CMD;
120                };
121
122            /^printf$/
123                and do { # this is a hackery and a mockery of printf :)
124                    shift @$toks;
125                    my $fh = \*STDOUT;
126                    if (@$toks && $toks->[0] eq '>&2') {
127                        shift @$toks;
128                        $fh = \*STDERR;
129                    }
130                    my $fmt = shift(@$toks) || '';
131                    ($fmt) = hack_off_quotes $fmt;
132                    $fmt = eval qq(sprintf "$fmt");
133
134                    printf {$fh} $fmt, @$toks;
135                    next CMD;
136                };
137
138            /^id$/
139                and do {
140                    print <<OUT;
141uid=1000(nick) gid=1000(nick) groups=4(adm),6(disk),20(dialout),24(cdrom),46(plugdev),105(lpadmin),115(admin),116(sambashare),122(libvirtd),1000(nick)
142OUT
143                    next CMD;
144                };
145
146            print "got: @$toks\n";
147        }
148    }
149}
150
151
152######################################################################
153
154$| = 1; # turn on autoflush
155
156# get passwd
157print q(user@nowhere's password: );
158my $input = <STDIN>;
159
160print "\nLast login: blah de da\n";
161
162shell %ENV, PS1 => '[user@nowhere]$ ';
163print "Connection to nowhere closed\n"
164