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