Date: Thu, 12 Jul 2001 15:31:22 -0700 From: Tim Kientzle <kientzle@acm.org> To: "Michael C . Wu" <keichii@peorth.iteration.net> Cc: keichii@FreeBSD.org, freebsd-bugs@FreeBSD.org Subject: Re: misc/28920: periodic scripts do not run on desktop systems that aren't always on Message-ID: <3B4E253A.9360EAAA@acm.org> References: <200107121939.f6CJd1n00272@freefall.freebsd.org> <3B4E21AF.1EEC97F8@acm.org> <20010712172912.A86119@peorth.iteration.net>
next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format. --------------36A625E697425A4831054448 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Something this simple hardly requires a 'tarball' ;-) Attached; enjoy. - Tim Kientzle "Michael C . Wu" wrote: > > On Thu, Jul 12, 2001 at 03:16:15PM -0700, Tim Kientzle scribbled: > | I would appreciate if you reconsider. As I mentioned > | in my original post, I do have complete source implementing > | this system if you'd like to take a more careful look at > | the basic concept. > > Hi Tim, > > Please show us a URL leading to a tarball of your code. > And I think that the best way to do this would be to make > a port in the Ports system. > > Thanks, > Michael > -- > Michael C. Wu +1-512-7757700 > keichii@{iteration.net|freebsd.org} --------------36A625E697425A4831054448 Content-Type: text/plain; charset=us-ascii; name="TBKKperiodic" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="TBKKperiodic" #!/usr/bin/perl5 # # Copyright (C) 1998-2001, Tim Kientzle. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of Tim Kientzle may not be used to endorse or promote # products derived from this software without specific prior written # permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR OR AUTHORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR AUTHORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # ########################################################################### # # Run periodic tasks. # # The problem: 'daily'/'weekly'/'monthly' scripts are typically # scheduled to run at a particular time, which doesn't work with # systems that aren't always running. # # The solution: This script. # * Each class of tasks is contained in a directory, # e.g., '/etc/periodic/daily' # * That directory contains a collection of tasks. # * Each task is a separate executable (usually a shell script) # * This script simply executes each task # * It uses the '/var/db/periodic.db' database to keep track of when each # job was last executed, so that it knows which jobs need to be run next # * If invoked as "periodic <class>", then all jobs in <class> # are executed immediately. # * If invoked as "periodic <class> nice", then only a single # task---the most overdue one---is executed. This task is # executed at a reduced priority. # # Normally, 'periodic daily' is invoked every night in the # wee hours, and 'periodic daily nice' is invoked every 20 minutes or so. # If the system is on all night, the daily tasks will be run # in the middle of the night; if not, they will still get run. # Similarly, 'periodic weekly' and 'periodic monthly' # are invoked from cron late at night, and 'periodic weekly nice' # and 'periodic monthly nice' are invoked at much shorter intervals. # # With this approach, tasks get run periodically even if the system # isn't always on at a particular time. # # This is designed to be invoked from cron via the following entries: # # # # # do scheduled hourly/daily/weekly/monthly maintenance # # # 0 * * * * root TBKKperiodic hourly # 0 2 * * * root TBKKperiodic daily # 30 3 * * 6 root TBKKperiodic weekly # 30 5 1 * * root TBKKperiodic monthly # # # # Perform any tasks that are overdue because the system wasn't on at 2am. # # # */20 * * * * root TBKKperiodic daily nice # 30 * * * * root TBKKperiodic weekly nice # 30 */2 * * * root TBKKperiodic monthly nice # # # March, 1998: Initial version # March 23, 1999: Made mostly compatible with FreeBSD 3.1 'periodic' # TODO: support 'local_periodic' variable from rc.conf # ########################################################################### use File::Find; use DB_File; use Fcntl; $periodicDbFile = "/var/db/periodic.db"; # # Get directory to use as base (argument to this script) # if(scalar(@ARGV) == 0) { print "Usage: $0 <service> <nice>\n" } $service = $ARGV[0]; # First argument: hourly, daily, weekly, or monthly if($ARGV[1] eq 'nice') { # second arg is optional 'nice' $nice = 1; } else { $nice = 0; } $jobsdir = "/etc/periodic/$service"; # # Set up parameters based on job class. # $maxjobs = 100; # Maximum number of jobs to run before exiting $niceness = 0; # How much to reduce priority if($service eq "hourly") { $period = 60*60; # Run jobs older than this (in seconds) } elsif($service eq "daily") { $period = 60*60*25; # Run jobs older than this (in seconds) } elsif($service eq "weekly") { $period = 60*60*24*8; # Run jobs older than this (in seconds) } elsif($service eq "monthly") { $period = 60*60*24*32; # Run jobs older than this (in seconds) } # # If 'nice', run just one job at reduced priority # if($nice) { $maxjobs = 1; # Maximum number of jobs to run before exiting $niceness = 15; # How much to reduce priority } else { $period /= 8; # Even without 'nice', don't run jobs too often } # # Useful constants # $rootuid=0; $defaultpriority = getpriority(0,0); # Usual priority $taskpriority = $defaultpriority + $niceness; # (Higher value = more nice) # Open the database marking when each task was last run if(!tie(%periodicDb,'DB_File',"$periodicDbFile", O_RDWR, 0644, $DB_File::DB_BTREE)) { # This section is partly a gaurd against changes to the DB library. # There's pressure on the Perl community to switch to the # newer but incompatible DB 2.x libraries. In case Perl gets # re-linked against an incompatible DB, this will burn the old # database and create a new one. print "Couldn't open $periodicDbFile ... creating new database.\n\n\n"; unlink "$periodicDbFile"; # Delete it in case it's there but trashed tie(%periodicDb,'DB_File',"$periodicDbFile", O_RDWR | O_CREAT, 0644, $DB_File::DB_BTREE) || die "Failed to create new database."; } # # Scan dir and determine which jobs are waiting to run # my(@taskrunlist); chdir "$jobsdir"; &find(\&find_task,"$jobsdir"); # # Determining if a task is ready to run is basically quite easy, # but I've included a few sanity/security checks along the way. # sub find_task { # Get info about file if(!(($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_))) {return} # Security: must be a file, owned by root, not writable by group/world if((!-f _) || ($uid != $rootuid) || ($mode & 022)) { return } # Sanity check: Must be readable and executable if(($mode & 0500) != 0500) { return } # Useful: don't execute '_norun' files, emacs backups, etc. if(/^.*_norun$/ || /^.*~$/ || /\#/ || /^\./) { return } # Decide if this task needs to be run again if(defined $periodicDb{$File::Find::name} && ($periodicDb{$File::Find::name} > time-$period)) { return } # Does need to be run, save the full filename. push @taskrunlist,$File::Find::name; return; } # Sort list so oldest jobs get run first @taskrunlist = sort { $periodicDb{$a} <=> $periodicDb{$b} } @taskrunlist; # # Run the jobs # foreach $task (@taskrunlist) { # Do this task &run_task($task); # Limit number of jobs run if(++$totaljobs >= $maxjobs) { last } } untie %periodicDb; exit; ########################################################################### sub run_task { my($task) = @_; # Log this activity system('logger','-p','cron.notice',"$service:",$task); # Run the task (with altered priority) and collect the output. setpriority 0,0,$taskpriority; if (open TASK, "$task 2>&1 |") { # Collect stdout and stderr @output = <TASK>; close TASK; # Mark when this task was last run $periodicDb{$task} = time; } else { @output = ("Couldn't execute $task\n"); } setpriority 0,0,$defaultpriority; # Abbreviate task name for reporting $task =~ s|.*/([^/]*)$|$1|; # Mail the output (if any) to root @filtered = grep { !/^\s+$/ } @output; # Don't mail a blank message if(scalar(@filtered) > 0) { # If there was output, mail it to root open(SENDMAIL,"|/usr/sbin/sendmail -F$service root"); print SENDMAIL "Subject: $service: $task\n"; print SENDMAIL "\n"; print SENDMAIL @output; # Send the original (with blank lines) close SENDMAIL; } } --------------36A625E697425A4831054448-- To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-bugs" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3B4E253A.9360EAAA>