Quick Links
Calendar
| | Sun | Mon | Tue | Wed | Thu | Fri | Sat
|
Categories
Archives
XML/RSS Feed
Links
Statistics
Total entries in this blog:
Total entries in this category:
Published On: May 10, 2004 10:28 PM
|
run_radmind script (updated)
Here is the run_radmind script I use. It's called
on demand at logout, and as a periodic task nightly.
First the script
itself: run_radmind.plNow,
some commentary (in
blue):#!/usr/bin/perl#################################################################################
run_radmind##
This script runs
radmind.##
Requires the radmind client tools (and a running server
obviously).# (See
http://rsug.itd.umich.edu/software/radmind)##
Can also be used with iHook# (See
http://rsug.itd.umich.edu/software/ihook)#This
script started out as a light edit of University of Utah's run_radmind script,
and I've added my own tweaks and changes. Of particular coolness was UofU's
execute_command subroutine, which allowed the output of the radmind tools to
appear in iHook's drawer AND be captured to a file, which is invaluable for
troubleshooting.# Copyright (c) 2002
University of Utah Student Computing
Labs.# All Rights
Reserved.##
Many, many modifications by Greg Neagle, Walt Disney Feature
Animation##
Permission to use, copy, modify, and distribute this software
and# its documentation for any
purpose and without fee is hereby
granted,# provided that the above
copyright notice appears in all copies
and# that both that copyright notice
and this permission notice appear# in
supporting documentation, and that the name of The
University# of Utah not be used in
advertising or publicity pertaining
to# distribution of the software
without specific, written prior#
permission. This software is supplied as is without expressed
or# implied warranties of any
kind.##################################################################################################################################################################
change these to match your
installation$rserver = "-h
radmind";$cksum =
"";$fsdiffpath =
"/";$ktcheckoutput =
"/usr/local/radmind/tmp/ktcheck_output.log";$ktcheckoutput_backup_number
= 5;$fsdiffoutput =
"/usr/local/radmind/tmp/fsdiff_output.log";$fsdiffoutput_backup_number
= 5;$ignoreList =
"/usr/local/radmind/etc/ignore";$lapplyinput
=
"/usr/local/radmind/tmp/diff.T";$lapplyoutput
=
"/usr/local/radmind/tmp/lapply_output.log";$lapplyoutput_backup_number
= 5;$max_tries =
1;This next bit of
code lets me use the same script when it is run at logout and when it is run by
cron overnight. In the first instance, I run without checksums so fsdiff is
faster. In the second instance, I can turn checksums on and presumably no-one
will be around to complain how long fsdiff
takes.# if the checksum trigger file
exists, run with checksumming
on$checksumTriggerFile =
"/var/radmind/client/.radmindWithChecksums";if
(-f $checksumTriggerFile)
{ $cksum = "-c
sha1"; unlink
$checksumTriggerFile;}$radmind_error
=
"/usr/local/radmind/tmp/radmind_error";$radmind_log
=
"/var/log/run_radmind.log";$errorlog
=
"/usr/local/radmind/tmp/radmind_error.log";$ktcheck
= "/usr/local/bin/ktcheck -c sha1 $rserver 2>
\"$errorlog\"";The
fsdiffCommand variable below takes advantage of a new fsdiff feature (-v) which
gives percent-done
feedback:$fsdiffCommand =
"/usr/local/bin/fsdiff -A $cksum -o $fsdiffoutput -v $fsdiffpath 2>
\"$errorlog\"";$lapply =
"/usr/local/bin/lapply -F $cksum $rserver $lapplyinput 2>
\"$errorlog\"";$restartCheckFile
=
"/usr/local/radmind/tmp/restartCheck.T";$rebootList
=
"/usr/local/radmind/etc/rebootList";$rebootIgnoreList
=
"/usr/local/radmind/etc/rebootIgnoreList";$updatesNeededFile
=
"/var/radmind/client/.updatesNeeded";$extensionsCheckFile
=
"/usr/local/radmind/tmp/extensionsCheck.T";$prefsPanesCheckFile
=
"/usr/local/radmind/tmp/prefsPanesCheck.T";$ktcheck_pic
=
"/usr/local/radmind/images/ktcheck.tif";$fsdiff_pic
=
"/usr/local/radmind/images/fsdiff.tif";$lapply_pic
=
"/usr/local/radmind/images/lapply.tif";I
discovered that when using radmind to upgrade a machine from 10.2.x to 10.3,
that sometimes it locked up during the lapply. Since (for me at least),
upgrading from 10.2.x to 10.3 via radmind takes over an hour, and I have cron
set to run tasks every 15 minutes, I thought perhaps something cron ran was
crashing as libraries and code was swapped out from under it. So I added code
in the run_radmind script to turn cron off. This seems to have improved the
reliability of the upgrade from 10.2.x to
10.3.#################################################################################
Stop cron so it doesn't call stuff we don't want to run during
radmind#$cronMsg
= `/System/Library/StartupItems/Cron/Cron
stop`;print
"$cronMsg\n";I can't
really tell if this next bit is doing anything: I still see the output from
fsdiff and lapply come in chunks with pauses in between, which looks like
buffering to
me...#################################################################################
Turn buffering
off#$|++;$oldhandle
= select( STDERR
);$|++;select(
$oldhandle );print "%UIMODE
AUTOCRATIC\n"; # iHook directive (This removes the menu bar in Panther - which
appears even when no-one is logged
in!)print "%BACKGROUND
$ktcheck_pic\n"; # iHook
directive.print "%BEGINPOLE\n"; #
iHook directive.Note
the indeterminant progress bar, since there's really no way to tell how far
along ktcheck
is.#################################################################################
Keep track of time, write to
log#$rightnow
= time; # keep track of timesystem
"echo \"--------------------------------\" >>
$radmind_log";system "date >>
$radmind_log";system "echo
\"run_radmind started\" >>
$radmind_log";#################################################################################
Roll
logs#&roll_log($ktcheckoutput,
$ktcheckoutput_backup_number);&roll_log($fsdiffoutput,
$fsdiffoutput_backup_number);&roll_log($lapplyoutput,
$lapplyoutput_backup_number);#################################################################################
prep stuffchdir
("/");# radmind loop$current_try = 1;while (1) { ################################################################################ # run ktcheck # print "Task 1 of 3: Checking for updates on the server\n"; $result = execute_command ("$ktcheck", $ktcheckoutput); if ($result == 0) { print "No updates from server\n"; } elsif ($result == 1) { print "Updates found\n"; system
"/usr/bin/touch
$updatesNeededFile"; } else { print "ktcheck encountered a fatal error: $result\n"; system "echo \"ktcheck encountered a fatal error: $result\" >> $radmind_log"; &radmindFailed; } print "%1\n"; # iHook directive. ################################################################################ # run fsdiff # print "Task 2 of 3: Examining the filesystem for differences\n"; print "%BACKGROUND $fsdiff_pic\n"; # iHook directive. $result = system "$fsdiffCommand"; if ($result != 0) { print "fsdiff encountered a fatal error: $result\n"; system "echo \"fsdiff encountered a fatal error: $result\" >> $radmind_log"; &radmindFailed; } This
next bit filters out any lines you don't want in the final lapply. I originally
used it to filter out .DS_Store files. The problem came when later you needed
to remove a directory. If that directory had any .DS_Store files within, they
had been removed from lapply's transcript and therefore lapply would try to
remove a directory that wasn't empty and it then failed. I planned to try to
write some code to work around that issue, but never got around to
it. ################################################################################ #
filter output from
fsdiff # system
"cat $fsdiffoutput | fgrep -v -f $ignoreList >
$lapplyinput";
print "%1\n";
# iHook directive. We
reset the progress bar to 1% (Setting it to 0% removes it from the window, which
is visually jarring!Now
we run lapply.
################################################################################ #
run
lapply # print
"Task 3 of 3: Applying
changes\n"; print
"%BACKGROUND $lapply_pic\n"; # iHook
directive. $fsdsize
= ( stat( $lapplyinput ))[ 7
]; if (
$fsdsize > 0 )
{We get the number
of lines in the applicable transcript so we can calculate (roughly) how far done
we are. See the execute_lapply subroutine for more of
this. $lapplyLineCount = int `wc -l $lapplyinput`; $result = execute_lapply ("$lapply", $lapplyoutput, $lapplyLineCount, "1"); if ($result == 0) { &radmindSuccessful; last; } elsif ($result == 1) { if ($current_try >= $max_tries) { print "lapply failed too many times: $result\n"; system "echo \"lapply failed too many times: $result\" >> $radmind_log"; &radmindFailed; } else { print "lapply failed, trying again: $result\n"; system "echo \"lapply failed, trying again: $result\" >> $radmind_log"; next; } } else { print "lapply encountered a fatal error - see log for errors.\n"; system "echo \"lapply failed: $result\" >> $radmind_log"; &radmindFailed; } } else { print "No changes found!\n"; system "echo \"No changes found\" >> $radmind_log"; &radmindSuccessful; last; } $current_try++;}This
is UofUtah's subroutine. I had never figured out pipe handles in Perl, so this
was an education for
me:################################################################################# This subroutine executes the radmind commands and saves the output to a log,# prints it to stdout, and saves stderr to a log as well.#sub execute_command { local ($thecommand, $path_to_log) = @_; open (COMMAND, "$thecommand |"); open (LOG, ">>$path_to_log"); while (<COMMAND>) { print STDERR; print LOG; } close (LOG); close (COMMAND); return $? >> 8;}This
subroutine, based on execute_command, has the changes needed to give
percent-done feedback to the
user:#################################################################################
This subroutine executes lapply and saves the output to a
log,# prints it to stdout, and saves
stderr to a log as well.# It also
provides percent-done feedback for use by
iHook.#sub
execute_lapply
{ local
($thecommand, $path_to_log, $fsdiff_line_count, $startPercent) =
@_; open
(COMMAND, "$thecommand
|"); open
(LOG,
">>$path_to_log"); $lapply_line_count
= 0; while
(<COMMAND>) {
print
STDERR; print
LOG;For each line
that comes back from lapply, we increment our counter. Since we know how many
lines are in the applicable transcript, and lapply returns one line of output
for (basically) each line in the applicable transcript, we have a pretty good
idea how far along we
are. $lapply_line_count++; if
( int ($lapply_line_count/10) == ($lapply_line_count/10) ) (This way we only
update every ten files or
so) { $ratio
=
$lapply_line_count/$fsdiff_line_count; if
( $ratio > 1 ) { $ratio = 1;
} $percentDone
= $startPercent + int
($ratio*(99-$startPercent)); print
"%$percentDone\n";Or you
can get fancy and do this: print "%percentDone Updated item $lapply_line_count
of
$fsdiff_line_count\n"; } } close
(LOG); close
(COMMAND); return
$? >>
8;}#################################################################################
If radmind was successful, do these
things#sub
radmindSuccessful
{ print
"Finishing
up\n"; #print
"%99\n"; # iHook directive
# How long
did radmind
take? $rightnowwer
=
time; $radmindtime
= $rightnowwer - $rightnow;
$message =
"Radmind successful and took $radmindtime
seconds"; system
"echo \"$message\" >>
$radmind_log"; print
"%100\n"; # iHook
directiveIn Jaguar,
we'd delete /System/Library/Extensions.kextcache and
/System/Library/Extensions.mkext to get the OS to rebuild its extensions cache
on restart. I've found that method to be unreliable with Panther. A method that
seems to work is to touch the /System/Library/Extensions directory. So we grep
the applicable transcript for "/System/Library/Extensions/", which would catch
any changes to the directory. It's important to grep for
"/System/Library/Extensions/", and not "/System/Library/Extensions" - since we
are touching /System/Library/Extensions, on the next radmind run, fsdiff will
notice that /System/Library/Extensions has changed, and will change it back.
Then our script would see /System/Library/Extensions in the transcript, touch
it, and so
on... #do we
need to update extensions
cache? #were
any kernel extensions added, removed, or
changed? system
"grep /System/Library/Extensions/ $lapplyinput >
$extensionsCheckFile"; #check
the extensionsCheckFile filesize. If it's greater than zero, touch
/System/Library/Extensions $extcfsize
= ( stat( $extensionsCheckFile ))[ 7
]; if (
$extcfsize > 0 )
{ system
"/usr/bin/touch
/System/Library/Extensions"; }We
do something similar with Preference Panes. The OS keeps a cache of Pref Panes.
When radmind adds or deletes a pane from either /System/Library/PreferencePanes
or /Library/PreferencePanes we need to touch /System/Library/PreferencePanes to
signal to the OS to update its cache. (Preference panes can also go in
~/Library/PreferencePanes. By grepping for "/Library/PreferencePanes/" we
actually catch all possible locations, though in practice we don't manage user
space) #do we
need to invalidate Preferences Panes
cache? #were
any prefs panes added, removed, or
changed? system
"grep /Library/PreferencePanes/ $lapplyinput >
$prefsPanesCheckFile"; #check
the prefsPanesCheckFile filesize. If it's greater than zero, touch
/System/Library/PreferencePanes $ppcfsize
= ( stat( $prefsPanesCheckFile ))[ 7
]; if (
$ppcfsize > 0 )
{ system
"/usr/bin/touch
/System/Library/PreferencePanes"; }I
don't like to restart after a radmind run unless it is needed. But how to tell
if it's needed? I grep through the applicable transcript and look for items
that trigger a restart. First we find all the items in the $rebootList, then
eliminate the items in the
$rebootIgnoreList.Here's
my current
$rebootList:/mach_kernel/System/Library//Library/StartupItems//Library/Preferences/DirectoryService//Library/Application\bSupport/Norton\bSolutions\bSupport/Here's
my current
$rebootIgnoreList/Library/Application\bSupport/Norton\bSolutions\bSupport/Scheduler/SymSecondaryLaunch.app/Contents/MacOS/SymSecondaryLaunch/Library/Application\bSupport/Norton\bSolutions\bSupport/Scheduler/schedLauncher/Library/Preferences/DirectoryService/.DSRunningSP/Library/StartupItems/NortonMissedTasks/NortonMissedTasks/System/Library/CoreServices/.disk_labelThe
idea here is to reboot if anything in the rebootList has changed, unless it is
in the rebootIgnoreList.So if
radmind just installs an application with all its files under /Applications, the
machine will not reboot. But if stuff in /System/Library/ changes, it generally
will reboot.
#check to see
if we need a
restart system
"fgrep -f $rebootList $lapplyinput | fgrep -v -f $rebootIgnoreList >
$restartCheckFile"; #check
the restartCheckFile filesize. If it's greater than zero,
restart $rscfsize
= ( stat( $restartCheckFile ))[ 7
]; if (
$rscfsize > 0 )
{ print
"Items installed require a restart. Restarting
now...\n"; system
"sleep 2";Note that
I don't actually restart in this script - instead I let the calling script
restart. This allows me to run this script manually remotely via SSH and not
get kicked off at the
end. #
we'll just fall through and exit normally and let the logout hook
script #
notice that the restartCheckFile exists and actually do the
restart. }
else
{ #if
we are not restarting we should restart
cron $cronmsg
= `/System/Library/StartupItems/Cron/Cron
start`; print
"$cronmsg\n"; }Since
we were successful, we can remove the trigger file that tells us updates were
needed. If the update failed, the file would not be
removed. if
(-e $updatesNeededFile) { unlink $updatesNeededFile
}I use a simple CGI
to report success to a webserver. I can then see when the last successful
radmind was for a given host. You can find details about this CGI elsewhere on
this website.
#check in
with the radmind check-in
cgi system
"curl
http://radmind/cgi-bin/radmindCheckIn"; exit
0;}#################################################################################
If radmind died, this is the cleanup
subroutine.#sub radmindFailed { # print error log to stdout open (LOGGY, "<$errorlog"); while (<LOGGY>) { print; } close (LOGGY); #system "touch $radmind_error"; # How long did radmind take? $rightnowwer = time; $radmindtime = $rightnowwer - $rightnow; # $message = "Radmind failed and took $radmindtime seconds"; system "echo \"$message\" >> $radmind_log"; system "cat \"$errorlog\" >> $radmind_log";We're
not restarting, so we should start cron back
up: # restart
cron $cronmsg
= `/System/Library/StartupItems/Cron/Cron
start`; print
"$cronmsg\n";If
radmind fails, I send mail to root - you may (probably will) want to send it
elsewhere. Of course this means sendmail/postfix must be properly configured on
the system. #
notify the
authorities $hostname
=
`hostname`; chomp
$hostname; system
"cat $errorlog | mail -s \"Radmind failed on $hostname.\"
root";
exit
1;}Unchanged
from
UofUtah:################################################################################# This script renames $path_to_log so that the lowest number is always the# newest. The oldest that is greater than $number_of_backups is deleted.#sub roll_log { local ($path_to_log, $number_of_backups) = @_; if (-e $path_to_log) { if (-e $path_to_log.".bak$number_of_backups") { unlink $path_to_log.".bak$number_of_backups"; } for ($i = $number_of_backups ; $i > 1 ; $i-- ) { if (-f $path_to_log.".bak".($i-1)) { system "/bin/mv -f \"$path_to_log".".bak".($i-1)."\" \"$path_to_log".".bak$i\""; } } system "/bin/mv -f \"$path_to_log\" \"$path_to_log".".bak1\""; }} Here's
the pictures referenced by the script, which because they are TIFFs with
transparency, look awful on a web page, but quite nice in
iHook: images.zip
Posted: Mon - May 10, 2004 at 09:22 PM
|