# $Id: Sudoscript.pm,v 1.5 2004/05/30 20:44:47 hbo Exp $ package Sudoscript; our $VERSION='0.1'; =pod =head1 NAME Sudoscript.pm =head1 SYNOPSIS use Sudoscript; my $ss=Sudoscript->new(); .. do stuff with $ss =head1 DESCRIPTION A module to implement some common attributes and methods shared between sudoshell and sudoscriptd. See L<sudoscript(8)>, L<sudoscriptd(8)> and L<sudoshell(1)> for descriptions of the sudoscript system. This module implements routines that =over 4 =item * Set up the execution environment based on the running OS =item * Return command names with switches tuned for the running OS =item * Check to see if sudoscriptd is running =item * Return date stamp strings in one of several formats =item * Close standard I/O channels for daemons, redirecting STDERR to log files. =back The following sections docment these routimnes more fully. =cut # use strict; use warnings; use POSIX qw(tzname); =head1 Constructor The constructor is called new(). It does the usual OO style initialization, then returns the result of calling _init(); =cut # The constructor sub new { my $class = shift; my $self= bless {}, ref($class) || $class; return $self->_init(@_); } =pod The _init() routine: =over 4 =item * Initializes the local time zone names =item * Sets a safe execution path =item * Checks the OS type of the running system =item * Sets up some shell commands based on the OS type =item * Returns its $self if it recognized the OS, or undef otherwise =back =cut # sub _init { my $self=shift; # Get time zone names POSIX::localtime(time); my @zones=tzname(); $self->{TZNAMES}=\@zones; # Set a safe PATH and define some external programs $ENV{PATH}="/bin:/usr/bin:/usr/sbin:/usr/local/bin"; $self->{GREP} = "grep"; $self->{SUDO}="sudo"; # These are OS dependent # OS dependencies my ($PS,$initscr,$script); if ($^O eq 'solaris'){# or $^O eq 'irix'){ $PS="ps -ef"; $initscr="/etc/init.d/sudoscriptd"; $script="script"; $ENV{PATH} .= ':/usr/freeware/bin:/usr/bsd' if $^O eq 'irix'; } elsif ($^O eq 'linux'){ $script="script -f"; # flush channels on linux (gnu script) $PS="ps auxww"; $initscr="/etc/init.d/sudoscriptd"; } elsif($^O eq 'freebsd' || $^O eq 'openbsd') { $PS="ps aux"; $initscr="/usr/local/etc/rc.d/sudoscriptd.sh"; $script="script"; } elsif($^O eq 'netbsd') { $ENV{PATH} .= ':/usr/pkg/bin:/usr/pkg/sbin'; $PS="ps aux"; $initscr="/usr/pkg/etc/rc.d/sudoscriptd"; $script="script"; } elsif($^O eq 'hpux') { $PS="ps -ef"; $initscr="/sbin/init.d/sudoscriptd"; $script="script"; } else { print <<'EOM'; Sorry, but your OS is not among the ones I support Currently, that's linux, solaris, hp-ux, freebsd, openbsd and netbsd. That's because those are the ones I or other contributors have access to. If you'd like support for your OS, either give me a root shell (!) on a representative system running your OS, or port it yourself and send me (hbo@egbok.com) the diffs. If system directory locations (i.e. /var/log and /var/run) don't have to change, and your system has Perl 5 and POSIX, That shouldn't be too hard. See the PORTING document in the distribution for details. EOM #' return undef; } $self->{PS}=$PS; $self->{INITSCR}=$initscr; $self->{SCRIPT}=$script; return $self; } =head1 Command Properties These properties return unqualified command names with switches, when they appear, appropriate for the running OS. (Since _init() sets the execution path, we do not fully qualify the paths to these commands.) =head2 SUDO() The sudo(8) command without any switches =cut sub SUDO { my $self=shift; return $self->{SUDO}; } =head2 GREP() The grep(1) command, without any switches =cut sub GREP { my $self=shift; return $self->{GREP}; } =head2 PS() The ps(1) command with switches that produce a listing parseable by checkpid() =cut sub PS { my $self=shift; return $self->{PS}; } =head2 INITSCR() The complete path to the sudoscriptd init script =cut sub INITSCR { my $self=shift; return $self->{INITSCR}; } =head2 SCRIPT() The script(1) command. On Linux, this will have the -q switch added. =cut sub SCRIPT { my $self=shift; return $self->{SCRIPT}; } =head2 TZNAMES() The local time zone names set up by _init() =cut sub TZNAMES { my $self=shift; return @{$self->{TZNAMES}}; } =head1 Methods =head2 check_ssd() This method checks to see if sudoscriptd is running (via checkpid()) If not, it offers to start it, and gives some helpful advice regarding starting sudoscriptd at boot time. It also tells the user they need root sudo privilege to successfully start sudoscriptd. The method then looks for the sudoscriptd init script set up by _init(). If that script is not found, or is not executable, the method prompts for an init script path to use. It then attempts to start the daemon. The method sleeps for three seconds and then checks again for sudoscriptd with checkpid(). If the daemon still isn't running, the method B<die>s, taking the caller with it. =cut sub check_ssd { my $self=shift; if (!$self->checkpid()){ my $ans; print "The sudoscriptd doesn't appear to be running!\n"; print "Would you like me to start it for you? (requires root sudo privilege)? "; $ans=<>; chomp $ans; die "Can't run sudoshell without sudoscriptd" if ($ans!~/^[Y|y]/); print <<'EOM'; This will be a one-off startup of the daemon. You may have to arrange for it to be started when the system starts, if that's what you want. See the INSTALL file in the distribution for details. EOM # bloody emacs syntax highlighting doesn't grok here docs my $initscr=$self->INITSCR(); if (! -x $initscr){ print "Hmm.. I can't seem to find the sudoscriptd startup file.\n"; print "Please tell me where it is. or just return for exit "; $ans=<>; chomp $ans; if (-x $ans) { $initscr=$ans; } else { die "Can't run sudoshell without sudoscriptd"; } } system($self->SUDO(), $initscr, "start",'&'); print "waiting for the daemon .."; sleep 3; print "done\n"; if (!$self->checkpid()){ print "Sorry, but I can't seem to start sudoscriptd for you!\n"; print "exiting\n"; exit; } } } =head2 checkpid() This method gets the PID of the current sudoscriptd out of /var/run/sudoscriptd.pid, if there is such a file. It looks for the PID in the process list If there is such a process, and its name contains 'sudoscriptd', the method returns the PID. Otherwise, it returns 0. =cut sub checkpid { my $self=shift; my $dpidf="/var/run/sudoscriptd.pid"; my $dpid; my @ret; my $gotone=0; if (-e $dpidf){ open DPID, $dpidf or die $!; $dpid=<DPID>; chomp $dpid; my $GREP=$self->GREP(); my $PS=$self->PS(); @ret=`$PS |$GREP $dpid | $GREP -v $GREP`; $gotone= ($#ret >-1 && (grep /sudoscriptd/,@ret)>0); } return $dpid if ($gotone); return 0; } =head2 datestamp() This method returns a date stamp string in one of three formats, depending on the passed parameter. These are: =over 4 =item long wdy mon dd hh:mm:ss TZ yyyy =item sortable yyyymoddhhmmss =item anything else wdy mon dd hh:mm:ss =back Where: wdy = week day name mon = three letter month name TZ = three letter time zone name (e.g. 'PST') yyyy = four digit year mo = two digit month number dd = two digit day of month hh = two digit hour mm = two digit minute ss = two digit second =cut sub datestamp { my $self=shift; # Sorta kinda syslog format my $DATEFMT = shift; my @zones=$self->TZNAMES(); my @monames=('Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec'); my @wdnames=('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); my ($s,$mi,$h,$d,$mo,$y,$wday,$yday,$isdst)=localtime(); if ($DATEFMT eq 'long'){ return sprintf "%3s %3s %02d %02d:%02d:%02d %3s %04d", $wdnames[$wday],$monames[$mo],$d,$h,$mi,$s,$zones[$isdst],$y+1900; } elsif ($DATEFMT eq 'sortable'){ return sprintf "%4d%02d%02d%02d%02d%02d", $y+1900,$mo+1,$d,$h,$mi,$s; } else { return sprintf "%3s %3s %02d %02d:%02d:%02d", $wdnames[$wday],$monames[$mo],$d,$h,$mi,$s; } } =head2 daemon_io() This method closes STDIN and STDOUT, and redirects STDERR to a file named: /var/run/sudoscriptd/stderr$tag Where $tag is a string passed to the method. There are commonly three, but sometimes two or four types of daemons running at all times in the sudoscript system. Each of them calls daemon_io() and each gets a seperate (per daemon type) stderr log. These are are overwritten on the next sudoscriptd startup. =cut sub daemon_io { my $self=shift; my $tag=shift; my $file="/var/run/sudoscript/stderr"; $file .=$tag if (defined $tag); close STDERR; open STDERR,">>$file"; close STDIN; close STDOUT; } =head1 SEE ALSO sudoscript(8) sudoscriptd(8) sudoshell(1) sudo(8) sudoers(5) http://www.egbok.com/sudoscript =head1 AUTHOR Howard Owen, E<lt>hbo@egbok.comE<gt> =head1 COPYRIGHT AND LICENSE Copyright 2003 by Howard Owen This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1;