File Coverage

blib/lib/Sudo.pm
Criterion Covered Total %
statement 12 139 8.6
branch 0 80 0.0
condition 0 36 0.0
subroutine 4 7 57.1
pod 1 3 33.3
total 17 265 6.4


line stmt bran cond sub pod time code
1             package Sudo;
2              
3 1     1   584027 use IPC::Run qw(run timeout start harness);
  1         64882  
  1         67  
4 1     1   889 use Term::ReadPassword;
  1         8433  
  1         79  
5 1     1   10 use base qw(Class::Accessor);
  1         8  
  1         1284  
6              
7 1     1   3157 use strict;
  1         2  
  1         2155  
8             our $VERSION = '0.33';
9              
10              
11             sub sudo_run
12             {
13             # Ok, glue the bits together.
14 0     0 1   my $self = shift;
15 0           my (%ret,$binary,$sudo,$program,$command,$sudo_args,$program_args);
16 0           my ($final_cmd,$sudo_pipe_handle,@cmd,$in,$out,$err,$line);
17 0           my ($username,$remote_machine,$remote_user);
18              
19             # Ok, IPC::Run is very (I mean very) sensitive to
20             # its @cmd processing. Each option has to be its own entry
21             # in the array. This means that options that flow in, if they
22             # are not of the form -\w but are -\w\s+\S+ (e.g. an option with
23             # an option parameter, such as -u username), have to be broken up
24             # into their own array elements in @cmd.
25             #
26             # Hopefully IPC::Run will be fixed one day. Until then, we write
27             # to the real API (not the documented one).
28             #
29              
30             # do we have a binary ...
31 0 0         if ($self->{debug})
32             {
33 0           $ENV{IPCRUNDEBUG}='basic';
34 0 0         if ($self->{debug} == 2)
    0          
35             {
36 0           $ENV{IPCRUNDEBUG}='data';
37             }
38             elsif ($self->{debug} >= 3)
39             {
40 0           $ENV{IPCRUNDEBUG}='details';
41             }
42             }
43 0 0         if (!defined($self->{sudo}))
44             {
45 0           %ret = (
46             'error' => 'Error: you did not tell me where the sudo binary is was not set'
47             );
48 0           return \%ret;
49             }
50            
51 0           $self->{sudo} =~ /^(\S+)$/; # force binary to be a
52             # single string with no
53             # spaces. This may break
54             # some folks paths, but it
55             # should be safer than allowing
56             # any command string.
57 0           $sudo = $1;
58            
59             # test for remote execution ... you need to have the ssh keys
60             # setup before this ...
61             #$remote_machine=$self->{hostname}if (defined($self->{hostname}));
62 0 0         if (defined($remote_machine))
63             {
64 0           $remote_user = getpwuid($<); # default user name is the user running the script
65 0 0         if (defined($self->{username}))
66             {
67 0           $remote_user = $self->{username};
68             }
69 0           push @cmd,"ssh";
70 0           push @cmd, (sprintf '%s@%s',$remote_user,$remote_machine);
71             }
72 0 0         if (!defined($remote_machine))
73             {
74 0 0         if (! -e $sudo )
75             {
76 0           %ret = (
77             'error' => (sprintf 'Error: the sudo binary "%s" does not exist',$sudo)
78             );
79 0           return \%ret;
80             }
81              
82 0 0         if (! -x $sudo )
83             {
84 0           %ret = (
85             'error' => (sprintf 'Error: the sudo binary "%s" is not executable',$sudo)
86             );
87 0           return \%ret;
88             }
89             }
90 0           push @cmd,$sudo;
91              
92             # force the -S switch to take the password from
93             # STDIN
94 0           push @cmd,'-S';
95            
96 0 0         if (!exists($self->{_timeout})) { $self->{_timeout} = 10; }
  0            
97            
98 0           $program_args = "";
99            
100             # ok, append the user information
101 0 0         if (!defined($self->{username}))
102             {
103 0           %ret = (
104             'error' => 'Error: username was not set'
105             );
106 0           return \%ret;
107             }
108            
109 0 0         if (exists ($self->{sudo_args}))
110             {
111             # process the arguments, splitting on white space
112 0           $self->{sudo_args} =~ s/^\s+//; # trim leading spaces
113 0           $self->{sudo_args} =~ s/\s+$//; # trim trailing spaces
114 0           push @cmd,(split(/\s+/,$self->{sudo_args}));
115             }
116            
117 0           push @cmd,"-u";
118 0           push @cmd,$self->{username};
119              
120 0 0         if (!defined($self->{program}))
121             {
122 0           %ret = (
123             'error' => 'Error: the program attribute was not set'
124             );
125 0           return \%ret;
126             }
127 0           $self->{program} =~ /^(\S+)$/; # force binary to be a
128             # single string with no
129             # spaces. This may break
130             # some folks paths, but it
131             # should be safer than allowing
132             # any command string.
133 0           $program = $1;
134 0 0 0       if (!defined($remote_machine) && (! -e $program ))
135             {
136 0           %ret = (
137             'error' => (sprintf 'Error: the program "%s" does not exist',$program)
138             );
139 0           return \%ret;
140             }
141              
142 0 0 0       if (!defined($remote_machine) && (! -x $program ))
143             {
144 0           %ret = (
145             'error' => (sprintf 'Error: the program "%s" is not executable',$program)
146             );
147 0           return \%ret;
148             }
149 0           push @cmd,$program;
150              
151 0 0         if (exists ($self->{program_args}))
152             {
153             # process the arguments, splitting on white space
154 0           $self->{program_args} =~ s/^\s+//; # trim leading spaces
155 0           $self->{program_args} =~ s/\s+$//; # trim trailing spaces
156             # Note: this might break some programs due to the
157             # multiple ways options may be specified and the actual
158             # command line argument contents. Hopefully it will be
159             # ok to start...
160 0           push @cmd,(split(/\s+/,$self->{program_args}));
161             }
162            
163            
164            
165             # ok, build the final "options" to the sudo we are going to run
166 0           $command = join(" ",@cmd);
167 0 0         printf STDERR "- command: %s\n",$command if ($self->{debug});
168 0 0 0       if (
169             exists($self->{debug}) &&
170             $self->{debug} >= 2
171             )
172             {
173 0           my $index=0;
174 0           foreach my $entry (@cmd)
175             {
176 0           printf STDERR "- _ [%s]: %s\n",$index,$entry;
177 0           $index++;
178             }
179             }
180 0 0 0       printf STDERR "\n< username = %s\n",$username if (
181             exists($self->{debug}) &&
182             $self->{debug} >= 2
183             );
184 0 0 0       printf STDERR "< password = %s\n",$self->{password} if (
185             exists($self->{debug}) &&
186             $self->{debug} >= 2
187             );
188 0 0 0       printf STDERR "< sudo = %s\n",$sudo if (
189             exists($self->{debug}) &&
190             $self->{debug} >= 2
191             );
192 0 0 0       printf STDERR "< program = %s\n",$program if (
193             exists($self->{debug}) &&
194             $self->{debug} >= 2
195             );
196 0 0 0       printf STDERR "< program args = %s\n",$program_args if (
197             exists($self->{debug}) &&
198             $self->{debug} >= 2
199             );
200            
201            
202 0 0         printf STDERR ": starting sudo \n" if ($self->{debug});
203            
204 0           $in=$self->{password};
205 0           $out="";
206 0           $err="";
207 0           my $h = run \@cmd,\$in,\$out,\$err,timeout($self->{_timeout});
208            
209            
210 0 0         printf STDERR "\n\n> output: %s \n> result: %s\n\n",$out,$? if ($self->{debug});
211            
212 0           %ret = (
213             'stdout' => $out,
214             'stderr' => $err,
215             'rc' => $h
216             );
217            
218 0           return \%ret;
219             }
220              
221             sub sudo_shell_start
222             {
223             # Ok, glue the bits together.
224 0     0 0   my $self = shift;
225 0           my (%ret,$binary,$sudo,$program,$command,$sudo_args,$program_args);
226 0           my ($final_cmd,$sudo_pipe_handle,@cmd,$in,$out,$err,$line);
227 0           my ($username,$remote_machine,$remote_user);
228              
229             # Ok, IPC::Run is very (I mean very) sensitive to
230             # its @cmd processing. Each option has to be its own entry
231             # in the array. This means that options that flow in, if they
232             # are not of the form -\w but are -\w\s+\S+ (e.g. an option with
233             # an option parameter, such as -u username), have to be broken up
234             # into their own array elements in @cmd.
235             #
236             # Hopefully IPC::Run will be fixed one day. Until then, we write
237             # to the real API (not the documented one).
238             #
239              
240             # do we have a binary ...
241 0 0         if ($self->{debug})
242             {
243 0           $ENV{IPCRUNDEBUG}='basic';
244 0 0         if ($self->{debug} == 2)
    0          
245             {
246 0           $ENV{IPCRUNDEBUG}='data';
247             }
248             elsif ($self->{debug} >= 3)
249             {
250 0           $ENV{IPCRUNDEBUG}='details';
251             }
252             }
253 0 0         if (!defined($self->{sudo}))
254             {
255 0           %ret = (
256             'error' => 'Error: you did not tell me where the sudo binary is was not set'
257             );
258 0           return \%ret;
259             }
260            
261 0           $self->{sudo} =~ /^(\S+)$/; # force binary to be a
262             # single string with no
263             # spaces. This may break
264             # some folks paths, but it
265             # should be safer than allowing
266             # any command string.
267 0           $sudo = $1;
268            
269 0 0         if (! -e $sudo )
270             {
271 0           %ret = (
272             'error' => (sprintf 'Error: the sudo binary "%s" does not exist',$sudo)
273             );
274 0           return \%ret;
275             }
276              
277 0 0         if (! -x $sudo )
278             {
279 0           %ret = (
280             'error' => (sprintf 'Error: the sudo binary "%s" is not executable',$sudo)
281             );
282 0           return \%ret;
283             }
284 0           push @cmd,$sudo;
285              
286             # force the -S switch to take the password from
287             # STDIN
288 0           push @cmd,'-S';
289              
290             # force the -s switch to use a shell
291 0           push @cmd,'-S';
292            
293            
294             # ok, append the user information
295 0 0         if (!defined($self->{username}))
296             {
297 0           %ret = (
298             'error' => 'Error: username was not set'
299             );
300 0           return \%ret;
301             }
302            
303 0 0         if (exists ($self->{sudo_args}))
304             {
305             # process the arguments, splitting on white space
306 0           $self->{sudo_args} =~ s/^\s+//; # trim leading spaces
307 0           $self->{sudo_args} =~ s/\s+$//; # trim trailing spaces
308 0           push @cmd,(split(/\s+/,$self->{sudo_args}));
309             }
310            
311 0           push @cmd,"-u";
312 0           push @cmd,$self->{username};
313              
314            
315             # ok, build the final "options" to the sudo we are going to run
316 0           $command = join(" ",@cmd);
317 0 0         printf STDERR "- command: %s\n",$command if ($self->{debug});
318 0 0 0       if (
319             exists($self->{debug}) &&
320             $self->{debug} >= 2
321             )
322             {
323 0           my $index=0;
324 0           foreach my $entry (@cmd)
325             {
326 0           printf STDERR "- _ [%s]: %s\n",$index,$entry;
327 0           $index++;
328             }
329             }
330 0 0 0       printf STDERR "\n< username = %s\n",$username if (
331             exists($self->{debug}) &&
332             $self->{debug} >= 2
333             );
334 0 0 0       printf STDERR "< password = %s\n",$self->{password} if (
335             exists($self->{debug}) &&
336             $self->{debug} >= 2
337             );
338 0 0 0       printf STDERR "< sudo = %s\n",$sudo if (
339             exists($self->{debug}) &&
340             $self->{debug} >= 2
341             );
342            
343 0 0         printf STDERR ": starting sudo \n" if ($self->{debug});
344            
345 0           $self->{in}=$self->{password};
346 0           $self->{out}="";
347 0           $self->{err}="";
348 0           $self->{sudo_handle} = start \@cmd,\$self->{in},\$self->{out},\$self->{err};
349            
350            
351 0 0         printf STDERR "\n\n> output: %s \n> result: %s\n\n",$out,$? if ($self->{debug});
352            
353             }
354              
355             sub sudo_shell_pump_nb
356             {
357 0     0 0   my ($self)=shift;
358 0           $self->{handle}->pump_nb;
359             }
360             1;
361             __END__