1# $Id: Sudoscript.pm,v 1.5 2004/05/30 20:44:47 hbo Exp $
2package Sudoscript;
3our $VERSION='0.1';
4
5=pod
6
7=head1 NAME
8
9Sudoscript.pm
10
11=head1 SYNOPSIS
12
13 use Sudoscript;
14 my $ss=Sudoscript->new();
15 .. do stuff with $ss
16
17=head1 DESCRIPTION
18
19A module to implement some common attributes and methods
20shared between sudoshell and sudoscriptd. See L<sudoscript(8)>,
21L<sudoscriptd(8)> and L<sudoshell(1)> for descriptions of the sudoscript system.
22
23This module implements routines that
24
25=over 4
26
27=item *
28
29Set up the execution environment based on the running OS
30
31=item *
32
33Return command names with switches tuned for the running OS
34
35=item *
36
37Check to see if sudoscriptd is running
38
39=item *
40
41Return date stamp strings in one of several formats
42
43=item *
44
45Close standard I/O channels for daemons, redirecting STDERR to log files.
46
47=back
48
49The following sections docment these routimnes more fully.
50
51=cut
52
53#
54use strict;
55use warnings;
56use POSIX qw(tzname);
57
58=head1 Constructor
59
60The constructor is called new(). It does the usual OO style initialization, then returns the
61result of calling _init();
62
63=cut
64
65# The constructor
66sub new {
67  my $class = shift;
68  my $self= bless {}, ref($class) || $class;
69  return $self->_init(@_);
70}
71
72=pod
73
74The _init() routine:
75
76=over 4
77
78=item *
79
80Initializes the local time zone names
81
82=item *
83
84Sets a safe execution path
85
86=item *
87
88Checks the OS type of the running system
89
90=item *
91
92Sets up some shell commands based on the OS type
93
94=item *
95
96Returns its $self if it recognized the OS, or undef otherwise
97
98=back
99
100=cut
101
102#
103
104sub _init {
105  my $self=shift;
106
107  # Get time zone names
108  POSIX::localtime(time);
109  my @zones=tzname();
110  $self->{TZNAMES}=\@zones;
111
112  # Set a safe PATH and define some external programs
113  $ENV{PATH}="/bin:/usr/bin:/usr/sbin:/usr/local/bin";
114
115  $self->{GREP} = "grep";
116  $self->{SUDO}="sudo";
117
118  # These are OS dependent
119  # OS dependencies
120  my ($PS,$initscr,$script);
121  if ($^O eq 'solaris'){# or $^O eq 'irix'){
122    $PS="ps -ef";
123    $initscr="/etc/init.d/sudoscriptd";
124    $script="script";
125    $ENV{PATH} .= ':/usr/freeware/bin:/usr/bsd' if $^O eq 'irix';
126  } elsif ($^O eq 'linux'){
127    $script="script -f"; # flush channels on linux (gnu script)
128    $PS="ps auxww";
129    $initscr="/etc/init.d/sudoscriptd";
130  } elsif($^O eq 'freebsd' || $^O eq 'openbsd' ||  $^O eq 'dragonfly') {
131    $PS="ps aux";
132    $initscr="/usr/local/etc/rc.d/sudoscriptd.sh";
133    $script="script";
134  } elsif($^O eq 'netbsd') {
135    $ENV{PATH} .= ':/usr/pkg/bin:/usr/pkg/sbin';
136    $PS="ps aux";
137    $initscr="/usr/pkg/etc/rc.d/sudoscriptd";
138    $script="script";
139  } elsif($^O eq 'hpux') {
140    $PS="ps -ef";
141    $initscr="/sbin/init.d/sudoscriptd";
142    $script="script";
143  } else {
144    print <<'EOM';
145Sorry, but your OS is not among the ones I support Currently, that's
146linux, solaris, hp-ux, freebsd, openbsd and netbsd. That's because those are the
147ones I or other contributors have access to. If you'd like support for your OS,
148either give me a root shell (!) on a representative system running your OS,
149or port it yourself and send me (hbo@egbok.com) the diffs. If system directory
150locations (i.e. /var/log and /var/run) don't have to change, and your system
151has Perl 5 and POSIX,  That shouldn't be too hard. See the PORTING document
152in the distribution for details.
153EOM
154#'
155
156    return undef;
157  }
158  $self->{PS}=$PS;
159  $self->{INITSCR}=$initscr;
160  $self->{SCRIPT}=$script;
161  return $self;
162}
163
164=head1 Command Properties
165
166These properties return unqualified command names with switches, when they appear,
167appropriate for the running OS. (Since _init() sets the execution path, we do not
168fully qualify the paths to these commands.)
169
170=head2 SUDO()
171
172The  sudo(8) command without any switches
173
174=cut
175
176sub SUDO {
177  my $self=shift;
178  return $self->{SUDO};
179}
180
181=head2 GREP()
182
183The grep(1) command, without any switches
184
185=cut
186
187sub GREP {
188  my $self=shift;
189  return $self->{GREP};
190}
191
192=head2 PS()
193
194The ps(1) command with switches that produce a listing parseable by checkpid()
195
196=cut
197
198sub PS {
199  my $self=shift;
200  return $self->{PS};
201}
202
203=head2 INITSCR()
204
205  The complete path to the sudoscriptd init script
206
207=cut
208
209sub INITSCR {
210  my $self=shift;
211  return $self->{INITSCR};
212}
213
214=head2 SCRIPT()
215
216The script(1) command. On Linux, this will have the -q switch added.
217
218=cut
219
220sub SCRIPT {
221  my $self=shift;
222  return $self->{SCRIPT};
223}
224
225=head2 TZNAMES()
226
227The local time zone names set up by _init()
228
229=cut
230
231sub TZNAMES {
232  my $self=shift;
233  return @{$self->{TZNAMES}};
234}
235
236=head1 Methods
237
238=head2 check_ssd()
239
240This method checks to see if sudoscriptd is running (via checkpid())
241If not, it offers to start it, and gives some helpful advice regarding starting sudoscriptd
242at boot time. It also tells the user they need root sudo privilege to successfully start
243sudoscriptd. The method then looks for the sudoscriptd init script set up
244by _init(). If that script is not found, or is not executable, the method prompts for
245an init script path to use. It then attempts to start the daemon. The method sleeps for
246three seconds and then checks again for sudoscriptd with checkpid(). If the daemon
247still isn't running, the method B<die>s, taking the caller with it.
248
249=cut
250
251sub check_ssd {
252  my $self=shift;
253  if (!$self->checkpid()){
254    my $ans;
255    print "The sudoscriptd doesn't appear to be running!\n";
256    print "Would you like me to start it for you? (requires root sudo privilege)? ";
257    $ans=<>;
258    chomp $ans;
259    die "Can't run sudoshell without sudoscriptd" if ($ans!~/^[Y|y]/);
260    print <<'EOM';
261This will be a one-off startup of the daemon. You may have
262to arrange for it to be started when the system starts, if that's
263what you want. See the INSTALL file in the distribution for details.
264EOM
265# bloody emacs syntax highlighting doesn't grok here docs
266    my $initscr=$self->INITSCR();
267    if (! -x $initscr){
268      print "Hmm.. I can't seem to find the sudoscriptd startup file.\n";
269      print "Please tell me where it is. or just return for exit ";
270      $ans=<>;
271      chomp $ans;
272      if (-x $ans) {
273	$initscr=$ans;
274      } else {
275	die "Can't run sudoshell without sudoscriptd";
276      }
277    }
278    system($self->SUDO(), $initscr, "start",'&');
279    print "waiting for the daemon ..";
280    sleep 3;
281    print "done\n";
282    if (!$self->checkpid()){
283      print "Sorry, but I can't seem to start sudoscriptd for you!\n";
284      print "exiting\n";
285      exit;
286    }
287  }
288}
289
290=head2 checkpid()
291
292This method gets the PID of the current sudoscriptd out of /var/run/sudoscriptd.pid,
293if there is such a file. It  looks for the PID in the process list
294If there is such a process, and its name contains 'sudoscriptd', the
295method returns the PID. Otherwise, it returns 0.
296
297=cut
298
299sub checkpid {
300  my $self=shift;
301  my $dpidf="/var/run/sudoscriptd.pid";
302  my $dpid;
303  my @ret;
304  my $gotone=0;
305  if (-e $dpidf){
306    open DPID, $dpidf or  die $!;
307    $dpid=<DPID>;
308    chomp $dpid;
309    my $GREP=$self->GREP();
310    my $PS=$self->PS();
311    @ret=`$PS |$GREP $dpid | $GREP -v $GREP`;
312    $gotone= ($#ret >-1 && (grep /sudoscriptd/,@ret)>0);
313  }
314  return $dpid if ($gotone);
315  return 0;
316}
317
318=head2  datestamp()
319
320This method returns a date stamp string in one of three formats,
321depending on the passed parameter.
322
323These are:
324
325=over 4
326
327=item long
328
329wdy mon dd hh:mm:ss TZ yyyy
330
331=item sortable
332
333yyyymoddhhmmss
334
335=item anything else
336
337wdy mon dd hh:mm:ss
338
339=back
340
341 Where:
342
343   wdy     = week day name
344   mon     = three letter month name
345   TZ      = three letter time zone name (e.g. 'PST')
346   yyyy    = four digit year
347   mo      = two digit month number
348   dd      = two digit day of month
349   hh      = two digit hour
350   mm      = two digit minute
351   ss      = two digit second
352
353=cut
354
355sub datestamp {
356  my $self=shift;
357  # Sorta kinda syslog format
358  my $DATEFMT = shift;
359  my @zones=$self->TZNAMES();
360  my @monames=('Jan','Feb','Mar','Apr','May','Jun',
361	       'Jul','Aug','Sep','Oct','Nov','Dec');
362  my @wdnames=('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
363  my ($s,$mi,$h,$d,$mo,$y,$wday,$yday,$isdst)=localtime();
364  if ($DATEFMT eq 'long'){
365    return sprintf "%3s %3s %02d %02d:%02d:%02d %3s %04d",
366      $wdnames[$wday],$monames[$mo],$d,$h,$mi,$s,$zones[$isdst],$y+1900;
367  } elsif ($DATEFMT eq 'sortable'){
368    return sprintf "%4d%02d%02d%02d%02d%02d",
369      $y+1900,$mo+1,$d,$h,$mi,$s;
370  } else {
371    return sprintf "%3s %3s %02d %02d:%02d:%02d",
372      $wdnames[$wday],$monames[$mo],$d,$h,$mi,$s;
373  }
374}
375
376=head2 daemon_io()
377
378This method closes STDIN and STDOUT, and redirects STDERR to a file named:
379
380  /var/run/sudoscriptd/stderr$tag
381
382Where $tag is a string passed to the method. There are commonly three, but sometimes
383two or four types of daemons running at all times in the sudoscript system. Each of them
384calls daemon_io() and each gets a seperate (per daemon type) stderr log. These are
385are overwritten on the next sudoscriptd startup.
386
387=cut
388
389
390sub daemon_io {
391  my $self=shift;
392  my $tag=shift;
393  my $file="/var/run/sudoscript/stderr";
394  $file .=$tag if (defined $tag);
395  close STDERR;
396  open STDERR,">>$file";
397  close STDIN;
398  close STDOUT;
399}
400
401=head1 SEE ALSO
402
403sudoscript(8)
404
405sudoscriptd(8)
406
407sudoshell(1)
408
409sudo(8)
410
411sudoers(5)
412
413http://www.egbok.com/sudoscript
414
415=head1 AUTHOR
416
417Howard Owen, E<lt>hbo@egbok.comE<gt>
418
419=head1 COPYRIGHT AND LICENSE
420
421Copyright 2003 by Howard Owen
422
423This library is free software; you can redistribute it and/or modify
424it under the same terms as Perl itself.
425
426=cut
427
4281;
429