File manager - Edit - /var/www/html/back/storage/app/public/92/2ubvqo/cron.tar
Back
NEWS.Debian.gz 0000644 00000001362 00000000000 0007006 0 ustar 00 � �T[o�0~�8��h��e���(h��n��� 9�Ib�ؑ/-��s�fc�З�v�w��9��hΧY��I��� h�Y��%[���+e���oS#8l���;0%x:�\�i�$���GH���G�vtP3]�XBiM���ك������JA� �G�Z�(�9`PH�0���;f� ���C����T�k8ʏ`H����MJ����M�F�/u�.4�A`ɂ�Ӄ��2�`JucpRs���^�Yjo;�%�C��٢\��^���� &r��8FϏ�.�ɕX� �*�c�����շ����8!ѫҧ �"hA�G�� ���t^2 ���-� ,���N� ��n��U�`��9��e>_��4�e�`��5�����mL��jb�����+p)�h=#�W��o���?N�k�m�E|�#{��1�* ���YtO��H��A�K@59R j��*}�Q �4^KbE���+�^:��8 Jj$��X ����;�vip����5T�u� NJ���vH�&��u_�5{J���UؐN �� ���v�ԓ�:��Q�oa����?�M�0;�Ukc�PԳ�e�����F�uȃ��ǭl#�[��8ܙ�{�S�����eB��.�i�iq���c8z��h %t&���X/!�A��RF�5;��'�=�1� ��KG��Cүx��[�n�=�郥RL8s�o�N���0�Ɩ��/�9:�IvB��w���6 copyright 0000644 00000007465 00000000000 0006454 0 ustar 00 Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: vixie-cron Upstream-Contact: Paul Vixie <paul@vix.com> Source: The original source for this package could be obtained from ftp://ftp.vix.com/pub/vixie/cron-3.0, although that link appears to have gone down. Files: * Copyright: 1988, 1990, 1993, 1994, Paul Vixie <paul@vix.com> 1994, Ian Jackson <ian@davenant.greenend.org.uk> 1996-2005, Steve Greenland <stevegr@debian.org> 2005-2014, Javier Fernández-Sanguino Peña <jfs@debian.org> 2010-2016, Christian Kastner <ckk@debian.org> Numerous contributions via the Debian BTS copyright their respective authors License: Paul-Vixie's-license Files: database.c Copyright: 1988, 1990, 1993, 1994 Paul Vixie <paul@vix.com> 1996-2005, Steve Greenland <stevegr@debian.org> 2003, Clint Adams <schizo@debian.org> 2010-2011, Christian Kastner <ckk@debian.org> 2011, Red Hat, Inc. License: Paul-Vixie's-license and GPL-2+ and ISC Files: debian/examples/cron-stats.pl Copyright: 2006, Javier Fernández-Sanguino Peña <jfs@debian.org> License: GPL-2+ Files: debian/examples/cron-tasks-review.sh Copyright: 2011, Javier Fernández-Sanguino Peña <jfs@debian.org> License: GPL-2+ Files: debian/examples/crontab2english.pl Copyright: 2001, Sean M. Burke License: Artistic License: Paul-Vixie's-license Distribute freely, except: don't remove my name from the source or documentation (don't take credit for my work), mark your changes (don't get me blamed for your possible bugs), don't alter or remove this notice. May be sold if buildable source is provided to buyer. No warranty of any kind, express or implied, is included with this software; use at your own risk, responsibility for damages (if any) to anyone resulting from the use of this software rests entirely with the user. License: GPL-2+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. . This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/> . On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". License: Artistic This program is free software; you can redistribute it and/or modify it under the terms of the "Artistic License" which comes with Debian. . THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. . On Debian systems, the complete text of the Artistic License can be found in "/usr/share/common-licenses/Artistic". License: ISC Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. . THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. changelog.Debian.gz 0000644 00000002463 00000000000 0010164 0 ustar 00 � �W�n7}�WL�[�v�]�n%�[%RbDJ�( j��q�-�U����_���$�EТ/��KΜ9s�\IgiU<�[�ja�oIYn��Z-��7ߕ�`uyrp�� _����f���TPUy�E9̈�� ���B������p�O�(:��i� +���M����,�xC�&$)���$J�[Y���)<����I~}� VVL/��U�P�w ���PQ�H�h�%�ftI�L� �FW܋�i���>�Y��b��p�`����/kBԁA���c�����Z-�>�<��<�1�'0W��'tƈ�Zh���^Ғ0�8��Fgx �<�1Z%yc��0�l��z�'��9��?����I�M��j/<P)�#��T�=u+ �@�)Q��Y�4�R���%R���{������%U�1˰���`P\��C���\ �?y2�X3[Ra��J��S<�T ��J-g�s��&���q��E2<4?���� ��~��md[?��T�6L��@SclB��c�CM՚��CL"�}E��ac ���ӚۤD�0�g\.��T�X�&�]���V|q���VNrD|����2'���������#d0&��pQ��H7r�*�i�fؐ��n�):ֈ~��J�rF�WG�U��'~ ƃ����qp��`Ms��s.�k-��=�h� �E��]=^*����3��@���,�|�H"3�� Ez��=ՠɜ�V��:j��:��d�Ν�5��*�pVQ�V��I0��yUqI ����s<�j��'?�j��06�K:���J��uC����)]z��������w�2�]zL�U;L���v�.�ܞ��g����F��#\z������.k$6��>�8��!k%D�c���f�c{k��1_mcѸ��� �@Sp�;.r��%��'k���%�h(�6|cW���&Qx�+�#W�H!��V�DŽ���ܻ �� �"<l]Q'Iھ<��u� ��@議m�"�[!�FZ4��X���$��N�$�ב�~E� �k��/r���}���k��=���Gl�Bd��&�k���ʙ���N?�a�D�W�a�����n!���u��� ����#%���s��s����V�@b����h�k��0��[+��'㼟t�q�������a|�ż�����i���� ��6��=)+N�/U�'�6 C�A��P��^���p<����'�ۇe�O[�Vz�r��~RLh�%ٟp8�w��=�ĉ���u�+���(fq�Z�)�L���� ���/x��� FEATURES 0000644 00000007570 00000000000 0005657 0 ustar 00 $Id: FEATURES,v 2.1 1993/12/28 08:34:43 vixie Exp $ Features of Vixie's cron relative to BSD 4.[23] and SysV crons: -- Environment variables can be set in each crontab. SHELL, USER, LOGNAME, and HOME are set from the user's passwd entry; all except USER can be changed in the crontab. PATH is especially useful to set there. TZ can be set, but cron ignores it other than passing it on through to the commands it runs. Format is variable=value Blanks surrounding the '=' will be eaten; other blanks in value are okay. Leading or trailing blanks can be preserved by quoting, single or double quotes are okay, just so they match. PATH=.:/bin:/usr/bin SHELL=/bin/sh FOOBAR = this is a long blanky example Above, FOOBAR would get "this is a long blanky example" as its value. SHELL and HOME will be used when it's time to run a command; if you don't set them, HOME defaults to your /etc/passwd entry and SHELL defaults to /bin/sh. MAILTO, if set to the login name of a user on your system, will be the person that cron mails the output of commands in that crontab. This is useful if you decide on BINMAIL when configuring cron.h, since binmail doesn't know anything about aliasing. -- Weekdays can be specified by name. Case is not significant, but only the first three letters should be specified. -- Months can likewise be specified by name. Three letters only. -- Ranges and lists can be mixed. Standard crons won't allow '1,3-5'. -- Ranges can specify 'step' values. '10-16/2' is like '10,12,14,16'. -- Sunday is both day 0 and day 7 -- apparently BSD and ATT disagree about this. -- Each user gets their own crontab file. This is a win over BSD 4.2, where only root has one, and over BSD 4.3, where they made the crontab format incompatible and although the commands can be run by non-root uid's, root is still the only one who can edit the crontab file. This feature mimics the SysV cron. -- The 'crontab' command is loosely compatible with SysV, but has more options which just generally make more sense. Running crontab with no arguments will print a cute little summary of the command syntax. -- Comments and blank lines are allowed in the crontab file. Comments must be on a line by themselves; leading whitespace is ignored, and a '#' introduces the comment. -- (big win) If the `crontab' command changes anything in any crontab, the 'cron' daemon will reload all the tables before running the next iteration. In some crons, you have to kill and restart the daemon whenever you change a crontab. In other crons, the crontab file is reread and reparsed every minute even if it didn't change. -- In order to support the automatic reload, the crontab files are not readable or writable except by 'crontab' or 'cron'. This is not a problem, since 'crontab' will let you do pretty much whatever you want to your own crontab, or if you are root, to anybody's crontab. -- If any output is generated by a command (on stdout OR stderr), it will be mailed to the owner of the crontab that contained the command (or MAILTO, see discussion of environment variables, above). The headers of the mail message will include the command that was run, and a complete list of the environment that was passed to it, which will contain (at least) the USER (LOGNAME on SysV), HOME, and SHELL. -- the dom/dow situation is odd. '* * 1,15 * Sun' will run on the first and fifteenth AND every Sunday; '* * * * Sun' will run *only* on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this is why we keep 'e->dow_star' and 'e->dom_star'. I didn't think up this behaviour; it's how cron has always worked but the documentation hasn't been very clear. I have been told that some AT&T crons do not act this way and do the more reasonable thing, which is (IMHO) to "or" the various field-matches together. In that sense this cron may not be completely similar to some AT&T crons. README.anacron 0000644 00000001145 00000000000 0007006 0 ustar 00 To ease coordination with anacron, the invocation of the run-parts for the /etc/cron.daily, /etc/cron.weekly, and /etc/cron.monthly directories was changed to the form test -e /usr/sbin/anacron || run-parts --report /etc/cron.daily What this means is that if anacron has been installed, it will be responsible for running those scripts. This is the standard configuration of anacron: if you simply install both cron and anacron, things will work as expected. However, if you have modified your anacron configuration (/etc/anacrontab), you may need to re-adjust it in order to accommodate this change. TODO.Debian 0000644 00000003150 00000000000 0006355 0 ustar 00 TODO stuff ---------- - Split of standard tasks to cron-standard, preliminary packages currently available at http://people.debian.org/~jfs/cron-standard but we need to handle the conffiles so that they don't get messed up See bug #257393 and Message-ID: <20050309142043.GB16617@dat.etsit.upm.es> - Better behave with cron replacements, investigate how cron packages (like fcron or bcron) can be installed/deinstalled without breaking the system. This might imply fixing #304036 (which might help fix #312614 too BTW) - Debate changing the policy of /etc/cron.d being a drop-in for packages only vs. a general drop-in for crontabs Milestones ---------- - Convert source package to source format 3.0 (quilt) The current cron source package is the result of a 1993 upstream and 17 years of commits. This organic growth must be normalized before an upgrade to upstream cron-4.1 can be considered. - Compare our resulting cron-3.0 patches to FreeBSD's where possible This is intended as a safety measure; the goal is to identify any grave errors or other larger issues in our patches so we don't carry them on into cron-4.1. - Review and update the ancient Debian packaging Certain elements are heavily outdated and must be either brought up-to-date, or dropped. - Update to upstream cron-4.1 - Compare our resulting cron-4.1 to RHEL/SLES, with a focus on possible security issues (SUID/SGID, SELinux, ...) - Compare our resulting cron-4.1 to Fedora's cronie (a fork of ISC cron) - Consider switching upstream to cronie, or at least grabbing some of it's features such as INOTIFY support. THANKS 0000644 00000003076 00000000000 0005426 0 ustar 00 15 January 1990 Paul Vixie Many people have contributed to cron. Many more than I can remember, in fact. Rich Salz and Carl Gutekunst were each of enormous help to me in V1; Carl for helping me understand UNIX well enough to write it, and Rich for helping me get the features right. John Gilmore wrote me a wonderful review of V2, which took me a whole year to answer even though it made me clean up some really awful things in the code. (According to John the most awful things are still in here, of course.) Paul Close made a suggestion which led to /etc/crond.pid and the mutex locking on it. Kevin Braunsdorf of Purdue made a suggestion that led to @reboot and its brothers and sisters; he also sent some diffs that lead cron toward compil- ability with System V, though without at(1) capabilities, this cron isn't going to be that useful on System V. Bob Alverson fixed a silly bug in the line number counting. Brian Reid made suggestions which led to the run queue and the source-file labelling in installed crontabs. Scott Narveson ported V2 to a Sequent, and sent in the most useful single batch of diffs I got from anybody. Changes attributable to Scott are: -> sendmail won't time out if the command is slow to generate output -> day-of-week names aren't off by one anymore -> crontab says the right thing if you do something you shouldn't do -> crontab(5) man page is longer and more informative -> misc changes related to the side effects of fclose() -> Sequent "universe" support added (may also help on Pyramids) -> null pw_shell is dealt with now; default is /bin/sh README.Debian 0000644 00000002745 00000000000 0006556 0 ustar 00 cron for DEBIAN ---------------------- This is the Debian GNU/Linux prepackaged and heavily modified version of Paul Vixie's cron subsystem. Debian's cron development is being done on Alioth: http://anonscm.debian.org/gitweb/?p=pkg-cron/cron.git This package was put together by Ian Jackson <iwj10@cus.cam.ac.uk>, from the standard sources to 3.0pl1, as posted to comp.sources.unix. Ian obtained them from src.doc.ic.ac.uk:/usenet/comp.sources.unix/volume27/vixie-cron. Debian's cron version introduces a significant number of changes: * Support for /etc/cron.d (drop-in dir for package crontabs) * Debian-specific file locations and commands * PAM support * SELinux support * auditlog support * DST and other time-related changes/fixes * SGID crontab(1) instead of SUID root * numerous other smaller features and fixes. Users are STRONGLY advised to carefully check the man pages crontab(1) and crontab(5). System administrators should also read cron(8). File locations that are different than that indicated in the cron distributions README: user crontabs: /var/spool/cron/crontabs/* log file: Check your syslog settings, facility "cron" allow file: /etc/cron.allow deny file: /etc/cron.deny This package is now maintained by Javier Fernández-Sanguino Peña <jfs@debian.org> and Christian Kastner <ckk@debian.org> Historical note: The old subversion repository from before the migration to git can be found here: http://svn.debian.org/wsvn/pkg-cron README 0000644 00000007030 00000000000 0005365 0 ustar 00 #/* Copyright 1988,1990,1993 by Paul Vixie # * All rights reserved # * # * Distribute freely, except: don't remove my name from the source or # * documentation (don't take credit for my work), mark your changes (don't # * get me blamed for your possible bugs), don't alter or remove this # * notice. May be sold if buildable source is provided to buyer. No # * warrantee of any kind, express or implied, is included with this # * software; use at your own risk, responsibility for damages (if any) to # * anyone resulting from the use of this software rests entirely with the # * user. # * # * Send bug reports, bug fixes, enhancements, requests, flames, etc., and # * I'll try to keep a version up to date. I can be reached as follows: # * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul # */ Vixie Cron V3.0 December 27, 1993 [V2.2 was some time in 1992] [V2.1 was May 29, 1991] [V2.0 was July 5, 1990] [V2.0-beta was December 9, 1988] [V1.0 was May 6, 1987] Paul Vixie [Note from Debian cron maintainer: This is the original README from the the vixie-cron package. The location of many cron files has been changed in order to comply with Debian policy and common sense -- look in the cron(8), crontab(1) and crontab(5) man pages for more info, as well as the README.Debian file in this directory.] This is a version of 'cron' that is known to run on BSD 4.[23] systems. It is functionally based on the SysV cron, which means that each user can have their own crontab file (all crontab files are stored in a read-protected directory, usually /var/cron/tabs). No direct support is provided for 'at'; you can continue to run 'atrun' from the crontab as you have been doing. If you don't have atrun (i.e., System V) you are in trouble. A messages is logged each time a command is executed; also, the files "allow" and "deny" in /var/cron can be used to control access to the "crontab" command (which installs crontabs). It hasn't been tested on SysV, although some effort has gone into making the port an easy one. This is more or less the copyright that USENET contributed software usually has. Since ATT couldn't use this version if they had to freely distribute source, and since I'd love to see them use it, I'll offer some rediculously low license fee just to have them take it. In the unlikely event that they do this, I will continue to support and distribute the pseudo-PD version, so please, don't flame me for wanting my work to see a wider distribution. To use this: Sorry, folks, there is no cutesy 'Configure' script. You'll have to go edit a couple of files... So, here's the checklist: Read all the FEATURES, INSTALL, and CONVERSION files Edit config.h Edit Makefile (both of these files have instructions inside; note that some things in config.h are definable in Makefile and are therefore surrounded by #ifndef...#endif) 'make' 'su' and 'make install' (you may have to install the man pages by hand) kill your existing cron process (actually you can run your existing cron if you want, but why?) build new crontabs using /usr/lib/{crontab,crontab.local} (either put them all in "root"'s crontab, or divide it up and rip out all the 'su' commands, collapse the lengthy lists into ranges with steps -- basically, this step is as much work as you want to make it) start up the new cron (must be done as root) watch it. test it with 'crontab -r' and watch the daemon track your changes. if you like it, change your /etc/{rc,rc.local} to use it instead of the old one. $Id: README,v 2.3 1993/12/28 08:34:43 vixie Exp $ examples/crontab2english.pl 0000644 00000066044 00000000000 0011756 0 ustar 00 #!/usr/bin/perl #Time-stamp: "2001-07-29 16:07:28 MDT" my $VERSION = '0.71'; require 5.00404; # I don't think there's (?: ... ) before that. =head1 NAME crontab2english -- explain crontab commands in English =head1 SYNOPSIS Usage: % crontab2english [-f] files... Or: % cat files... | crontab2english If you do just this: % crontab2english then it's the same as crontab -l | crontab2english Example output: % crontab2english | less Setting env var MAILTO to hulahoops@polygon.int Command: (line 2) Run: /bin/csh -c 'perl ~/thang.pl | mail -s hujambo root' At: 8:10am on the 15th of every month Command: (line 5) Run: df -k At: 5:40am every day Command: (line 7) Run: ls -l /tmp At: 6:50am every Monday Or with the -f ("f" for filter) switch, it just adds comments to the input file: % crontab2english -f | less # My happy crontab file MAILTO=hulahoops@polygon.int 10 8 15 * * /bin/csh -c 'perl ~/thang.pl | mail -s hujambo root' #> At: 8:10am on the 15th of every month 40 5 * * * df -k #> At: 5:40am every day 50 6 * * 1 ls -l /tmp #> At: 6:50am every Monday =head1 DESCRIPTION It's easy to make mistakes in crontab files. Running C<crontab2english> on your crontab files and reading the resulting English explanations will help you catch errors. =head1 SWITCHES C<-f> puts this in "filter mode" -- the output is just the input plus commentary. C<-v> describes the current C<crontab2english> version to STDOUT and exits. C<-p> forces POSIX-only mode: anything not allowed in the POSIX crontab spec won't be understood. C<-e> (usually default) turns off POSIX-only mode: i.e., it doesn't feign ignorance of things not in the POSIX spec. C<--> signals end of switches. =head1 ENVIRONMENT If the evironment variables C<POSIXLY_CORRECT> and/or C<POSIX_ME_HARDER> are true, then this turns on C<-p> (POSIX-only) mode by default. That's overrideable with the C<-e> switch. =head1 CAVEATS I've tried to make this program understand all the kinds of crontab lines that are out there. That probably includes a few kinds of lines that your particular cron daemon doesn't understand, so just because crontab2english understands something doesn't mean your cron daemon will. Pragmatically, however, there seem to be three kinds of cron daemons around these days: =over =item * Ones based on old (1993ish) Vixie crontab. These understand all of POSIX, and more. This is what almost almost everyone runs these days. =item * Ones that understand I<only> what the POSIX crontab spec allows -- which excludes all sorts of amenities including: stepped ranges ("1-9/2"), "VAR=NAME" lines, English month or day abbreviations ("mon" or "jan"), day 7 meaning "Sunday", and "*/3" meaning "every third...". =item * Even more recent (post-1993ish) Vixies. These seem relatively rare. They seem to be just like old Vixies, plus they understand at-words like "@annually", "@reboot", etc. (altho in some cases, those aren't mentioned in the docs!). =back There I<could> be some ancient or demented pre-Vixie non-POSIX crons running somewhere. One hopes that these would all basically understand anything that POSIX does (and possibly nothing more?), but you just might find peculiarities including: =over =item * Rejecting 0 for Sunday, and accepting only 7. =item * Rejecting 7 for Sunday, and accepting only 0. =item * Accepting lists I<or> ranges, but no lists that include ranges. I.e., allowing "7,8,9", allowing "1-3", but I<not> allowing "1-3,7-9". =item * Not accepting ranges at all? =back Consult your man pages carefully. Good general advice: keep your crontab lines simple, and that'll minimize the chances of disagreement between what you indend, what crontab2english understands (with or without C<-p>), and what your cron daemon understands. =head1 SEE ALSO C<man 1 crontab> C<man 5 crontab> C<man 8 cron> =head1 BUG REPORTS If this program explains a crontab line wrong, or can't parse it, then email me the line, and an explanation of how it you think it should parse. =head1 DISCLAIMER C<crontab2english> is distributed in the hope that it will be useful, but B<without any warranty>; without even the implied warranty of B<merchantability> or B<fitness for a particular purpose>. =head1 COPYRIGHT Copyright 2001 Sean M. Burke. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 AUTHOR Sean M. Burke, E<lt>sburke@cpan.orgE<gt> =head1 README Translates crontab notation into English, for sanity checking: For example, "10 8 15 * * foo bar" into: Run: foo bar with input "baz\x0a" At: 8:10am on the 15th of every month =head1 SCRIPT CATEGORIES UNIX/System_administration =head1 CHANGE LOG =over =item v0.71: 2001-07-29 Now supports the weird new Vixie-isms like "C<@annually>". Features for feigning ignorance of non-POSIX features: C<-p>, C<-e>, and the evironment variables C<POSIXLY_CORRECT> and/or C<POSIX_ME_HARDER>. =item v0.63: 2001-07-17 Fixed a bug spotted by Greg Wimpey, where leading whitespace wasn't getting duly ignored. =item v0.62: 2001-07-14 Added special cases for when minutes field is 0. Added explicit "require" statement to ensure acceptable Perl version. Changed "Every Tuesday of May" to "Every Tuesday in May" Changed qr//'s to just plain strings, for all the 500404 dinosaurs. =item v0.61 2001-01-23 First public release. =back =cut use strict; use integer; use constant 'DEBUG' => 0; my $filter = ''; # ...which is false. # Lame switch processing: my $posix; # whether to be just POSIX while(@ARGV and $ARGV[0] =~ m<^->s) { if($ARGV[0] eq '--') { # end of switches. shift @ARGV; last; } elsif($ARGV[0] eq '-f') { # filter mode shift @ARGV; $filter = '#> '; # ...which is true! } elsif($ARGV[0] eq '-p') { # disable extensions shift @ARGV; $posix = 1; } elsif($ARGV[0] eq '-e') { # enable extensions shift @ARGV; $posix = 0; } elsif($ARGV[0] eq '-v') { print "crontab2english v$VERSION sburke\@cpan.org\n"; exit; } else { die "Usage: crontab2english [-f] [files]\n" . "See 'perldoc crontab2english' for more info.\n"; } } $posix = $ENV{'POSIXLY_CORRECT'} || $ENV{'POSIX_ME_HARDER'} unless defined $posix; print $filter, " POSIX-only mode\n" if $posix; my @lines; if(@ARGV) { @lines = <>; } elsif(-t *STDIN) { @lines = `crontab -l`; } else { @lines = <STDIN>; } #-------------------------------------------------------------------------- # Build tables. my @dows = qw(Sun Mon Tue Wed Thu Fri Sat); my @months = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); my($dow, $month, %dow2num, %month2num, %num2dow, %num2month); my %mil2ampm; @mil2ampm{0 .. 23} = ('midnight', map($_ . 'am', 1 .. 11), 'noon', map($_ . 'pm', 1 .. 11)); @dow2num{map lc($_), @dows} = (0 .. 6); push @dows, 'Sun' unless $posix; # POSIX doesn't know about day 7 @num2dow{0 .. $#dows} = @dows; DEBUG and print "DOWS: @dows\n"; @month2num{map lc($_), @months} = (1 .. 12); @num2month{1 .. 12} = @months; unshift @months, ''; { my $x = join '|', map quotemeta($_), @dows; $dow = "^($x)\$"; # regexp $x = join '|', map quotemeta($_), @months; $month = "^($x)\$"; # regexp } my(%num2month_long, %num2dow_long); @num2month_long{1 .. 12} = qw( January February March April May June July August September October November December ); @num2dow_long{0 .. 6} = qw( Sunday Monday Tuesday Wednesday Thursday Friday Saturday ); $num2dow_long{7} = 'Sunday' unless $posix; my $atom; if($posix) { $atom = '\d+|(?:\d+-\d+)'; # will be a RE # Yes, POSIX allows no stepped ranges. } else { $atom = '\d+|(?:\d+-\d+(?:/\d+)?)'; # will be a RE } my $atoms = "^(?:$atom)(?:,$atom)*\$"; # well be a RE print "Atoms RE: $atoms\n" if DEBUG; my %atword = ( # for latter-day Vixie-isms 'reboot' => 'At reboot', 'yearly' => 'Yearly (midnight on January 1st)', 'annually' => 'Yearly (midnight on January 1st)', 'monthly' => 'Monthly (midnight on the first of every month)', 'weekly' => 'Weekly (midnight every Sunday)', 'daily' => 'Daily, at midnight', 'midnight' => 'Daily, at midnight', 'hourly' => 'At the top of every hour', # These are no longer documented in Vixie cron 3.0. Why not? ); #-------------------------------------------------------------------------- my $line_number = 0; { my(@bits,$k,$v); foreach (@lines) { print $_ if $filter; chomp; DEBUG > 1 and print "Line: <$_>\n"; ++$line_number; next if m/^[ \t]*#/s or m/^[ \t]*$/s; s/^[ \t]+//s; # "leading spaces and tabs are ignored" if(DEBUG > 1) { @bits = split m/[ \t]+/,$_,6; print "Bit count: ", scalar(@bits), ".\n"; } # The POSIX cron spec doesn't seem to mention # environment-setting lines at all! if(!$posix and m/^([^= \t]+)[ \t]*=[ \t]*\"(.*)\"[ \t]*$/s ) { # NAME = "VALUE" $k =~ s/[ \t]+$//; $filter or print "Setting env var $k to \"$v\"\n"; } elsif(!$posix and m/^([^= \t]+)[ \t]*=[ \t]*\'(.*)\'[ \t]*$/s ) { # NAME = 'VALUE' ($k,$v) = ($1,$2); #$k =~ s/[ \t]+$//; $filter or print "Setting env var $k to \'$v\'\n"; } elsif(!$posix and m/^([^= \t]+)[ \t]*=(.*)/s ) { # NAME = VALUE ($k,$v) = ($1,$2); #$k =~ s/[ \t]+$//; $v =~ s/^[ \t]+//; $filter or print "Setting env var $k to $v\n\n"; } elsif(!$posix and m/^\@(\w+)[ \t]+(.*)/s and exists $atword{lc $1}) { process_command($_, $atword{lc $1}, $2); } elsif( (@bits = split m/[ \t]+/, $_, 6) and @bits == 6 ) { DEBUG and print "Bits: ", map("<$_> ", @bits), "\n"; process_command($_, @bits); } else { if($filter) { print $filter, "UNPARSEABLE LINE?!\n"; } else { print "Unparseable line (#$line_number): \"", esc($_), "\"\n"; } } } exit; } #-------------------------------------------------------------------------- sub process_command { # 0 m, 1 h, 2 day-of-month, 3 month, 4 dow my $line = shift; my(@time_lines, $command_string); if(@_ == 2) { # hack for funky vixieism $command_string = $_[1]; @time_lines = ($_[0]); } else { # a normal line -- expand and Englishify it my(@bits) = expand_time_bits(@_); if(@bits == 1) { # signals error condition my $x = $bits[0]; if($filter) { print $filter, "Unparseable ", @$x == 1 ? 'bit' : 'bits', ": ", join(' ', map "\"$_\"", @$x), "\n" ; } else { print "Unparseable ", @$x == 1 ? 'bit' : 'bits', " in parsing of command ${$x}[-1] at line number $line_number:\n", map(" $_\n", @$x), "\n" ; } return; } @time_lines = bits_to_english(@bits); $time_lines[0] = ucfirst($time_lines[0]); if(length(join ' ', @time_lines) <= 75) { @time_lines = (join ' ', @time_lines); } for(@time_lines) { $_ = ' ' . $_ }; # indent over $time_lines[0] = "At:" . $time_lines[0]; $command_string = pop @bits; } my @command = split( "\n", percent_proc($command_string), -1 ); if(@command) { pop @command if @command == 2 and $command[1] eq ''; # Eliminate mention of basically null input } else { push @command, ''; } if(@command > 1) { my $x = join "\n", splice @command, 1; push @command, " with input \"" . esc($x) . "\""; } if($command[0] =~ m<^\*>s) { push @command, " (Do you really mean the command to start with \"*\"?)"; } elsif($command[0] eq '') { push @command, " (Do you really mean to run a null command?)"; } $command[0] = "Run: $command[0]"; if($filter) { print map("$filter $_\n", (@command == 1) ? () : (@command), # be concise for simple cases @time_lines ), ; } else { print #was: "Command: (line $line_number) $line\n", # but that's awful verbose "Command: (line $line_number)\n", map(" $_\n", @command, @time_lines ), "\n"; } return; } #-------------------------------------------------------------------------- sub expand_time_bits { my @bits = @_; my @unparseable; # 0 m, 1 h, 2 day-of-month, 3 month, 4 dow unless($posix) { if($bits[3] =~ m/($month)/oi) { $bits[3] = $month2num{lc $1} } if($bits[4] =~ m/($dow)/oi ) { $bits[4] = $dow2num{lc $1} } } for(my $i = 0; $i < 5 ; ++$i) { my @segments; if($bits[$i] eq '*') { push @segments, ['*']; } elsif(!$posix and $bits[$i] =~ m<^\*/(\d+)$>s) { # a hack for "*/3" etc push @segments, ['*', 0 + $1]; } elsif($bits[$i] =~ m/$atoms/ois) { foreach my $thang (split ',', $bits[$i]) { if($thang =~ m<^(?:(\d+)|(?:(\d+)-(\d+)(?:/(\d+))?))$>s) { if(defined $1) { push @segments, [0 + $1]; # "7" } elsif(defined $4) { push @segments, [0 + $2, 0 + $3, 0 + $4]; # "3-20/4" } else { push @segments, [0 + $2, 0 + $3]; # "3-20" } } else { warn "GWAH? thang \"$thang\""; } } } else { push @unparseable, sprintf "field %s: \"%s\"", $i + 1, esc($bits[$i]); next; } $bits[$i] = \@segments; } return \@unparseable if @unparseable; return @bits; } #-------------------------------------------------------------------------- sub bits_to_english { # This is the deep ugly scary guts of this program. # The older and eldritch among you might recognize this as sort of a # parody of bad old Lisp style of data-structure handling. my @bits = @_; my @time_lines; { # gratuitous block. # Render the minutes and hours ######################################## if(@{$bits[0]} == 1 and @{$bits[1]} == 1 and @{$bits[0][0]} == 1 and @{$bits[1][0]} == 1 and $bits[0][0][0] ne '*' and $bits[1][0][0] ne '*' # It's a highly simplifiable time expression! # This is a very common case. Like "46 13" -> 1:46pm # Formally: when minute and hour are each a single number. ) { my $h = $bits[1][0][0]; if($bits[0][0][0] == 0) { # Simply at the top of the hour, so just call it by the hour name. push @time_lines, $mil2ampm{$h}; } else { # Can't say "noon:02", so use an always-numeric time format: push @time_lines, sprintf '%s:%02d%s', ($h > 12) ? ($h - 12) : $h, $bits[0][0][0], ($h >= 12) ? 'pm' : 'am'; } $time_lines[-1] .= ' on'; } else { # It's not a highly simplifiable time expression # First, minutes: if($bits[0][0][0] eq '*') { if(1 == @{$bits[0][0]} or $bits[0][0][1] == 1) { push @time_lines, 'every minute of'; } else { push @time_lines, 'every ' . freq($bits[0][0][1]) . ' minute of'; } } elsif( @{$bits[0]} == 1 and $bits[0][0][0] == 0 ) { # It's just a '0'. Ignore it -- instead of bothering # to add a "0 minutes past" } elsif( !grep @$_ > 1, @{$bits[0]} ) { # it's all like 7,10,15. conjoinable push @time_lines, conj_and(map $_->[0], @{$bits[0]}) . ( $bits[0][-1][0] == 1 ? ' minute past' : ' minutes past' ); } else { # it's just gonna be long. my @hunks; foreach my $bit (@{$bits[0]}) { if(@$bit == 1) { #"7" push @hunks, $bit->[0] == 1 ? '1 minute' : "$bit->[0] minutes"; } elsif(@$bit == 2) { #"7-9" push @hunks, sprintf "from %d to %d %s", @$bit, $bit->[1] == 1 ? 'minute' : 'minutes'; } elsif(@$bit == 3) { # "7-20/2" push @hunks, sprintf "every %d %s from %d to %d", $bit->[2], $bit->[2] == 1 ? 'minute' : 'minutes', $bit->[0], $bit->[1], ; } } push @time_lines, conj_and(@hunks) . ' past'; } # Now hours if($bits[1][0][0] eq '*') { if(1 == @{$bits[1][0]} or $bits[1][0][1] == 1) { push @time_lines, 'every hour of'; } else { push @time_lines, 'every ' . freq($bits[1][0][1]) . ' hour of'; } } else { my @hunks; foreach my $bit (@{$bits[1]}) { if(@$bit == 1) { # "7" push @hunks, $mil2ampm{$bit->[0]} || "HOUR_$bit->[0]??"; } elsif(@$bit == 2) { # "7-9" push @hunks, sprintf "from %s to %s", $mil2ampm{$bit->[0]} || "HOUR_$bit->[0]??", $mil2ampm{$bit->[1]} || "HOUR_$bit->[1]??", } elsif(@$bit == 3) { # "7-20/2" push @hunks, sprintf "every %d %s from %s to %s", $bit->[2], $bit->[2] == 1 ? 'hour' : 'hours', $mil2ampm{$bit->[0]} || "HOUR_$bit->[0]??", $mil2ampm{$bit->[1]} || "HOUR_$bit->[1]??", } } push @time_lines, conj_and(@hunks) . ' of'; } # End of hours and minutes } # Day-of-month ######################################################## if($bits[2][0][0] eq '*') { $time_lines[-1] =~ s/ on$//s; if(1 == @{$bits[2][0]} or $bits[2][0][1] == 1) { push @time_lines, 'every day of'; } else { push @time_lines, 'every ' . freq($bits[2][0][1]) . ' day of'; } } else { my @hunks; foreach my $bit (@{$bits[2]}) { if(@$bit == 1) { # "7" push @hunks, 'the ' . ordinate($bit->[0]); } elsif(@$bit == 2) { # "7-9" push @hunks, sprintf "from the %s to the %s", ordinate($bit->[0]), ordinate($bit->[1]), } elsif(@$bit == 3) { # "7-20/2" push @hunks, sprintf "every %d %s from the %s to the %s", $bit->[2], $bit->[2] == 1 ? 'day' : 'days', ordinate($bit->[0]), ordinate($bit->[1]), } } # collapse the "the"s, if all the elements have one if(@hunks > 1 and !grep !m/^the /s, @hunks) { for (@hunks) { s/^the //s; } $hunks[0] = 'the '. $hunks[0]; } push @time_lines, conj_and(@hunks) . ' of'; } # Month ############################################################### if($bits[3][0][0] eq '*') { if(1 == @{$bits[3][0]} or $bits[3][0][1] == 1) { push @time_lines, 'every month'; } else { push @time_lines, 'every ' . freq($bits[3][0][1]) . ' month'; } } else { my @hunks; foreach my $bit (@{$bits[3]}) { if(@$bit == 1) { # "7" push @hunks, $num2month_long{$bit->[0]} || "MONTH_$bit->[0]??" } elsif(@$bit == 2) { # "7-9" push @hunks, sprintf "from %s to %s", $num2month_long{$bit->[0]} || "MONTH_$bit->[0]??", $num2month_long{$bit->[1]} || "MONTH_$bit->[1]??", } elsif(@$bit == 3) { # "7-20/2" push @hunks, sprintf "every %d %s from %s to %s", $bit->[2], $bit->[2] == 1 ? 'month' : 'months', $num2month_long{$bit->[0]} || "MONTH_$bit->[0]??", $num2month_long{$bit->[1]} || "MONTH_$bit->[1]??", } } push @time_lines, conj_and(@hunks); # put in semicolons in the case of complex constituency #if($time_lines[-1] =~ m/every|from/) { # $time_lines[-1] =~ tr/,/;/; # s/ (and|or)\b/\; $1/g; #} } # Weekday ############################################################# # # # # # From man 5 crontab: # Note: The day of a command's execution can be specified by two fields # -- day of month, and day of week. If both fields are restricted # (ie, aren't *), the command will be run when either field matches the # current time. For example, "30 4 1,15 * 5" would cause a command to # be run at 4:30 am on the 1st and 15th of each month, plus every Friday. # # [But if both fields ARE *, then it just means "every day". # and if one but not both are *, then ignore the *'d one -- # so "1 2 3 4 *" means just 2:01, April 3rd # and "1 2 * 4 5" means just 2:01, on every Friday in April # But "1 2 3 4 5" means 2:01 of every 3rd or Friday in April. ] # # # # # And that's a bit tricky. if($bits[4][0][0] eq '*' and ( @{$bits[4][0]} == 1 or $bits[4][0][1] == 1 ) ) { # Most common case -- any weekday. Do nothing really. # # Hm, does "*/1" really mean "*" here, given the above note? # # Tidy things up while we're here: if($time_lines[-2] eq "every day of" and $time_lines[-1] eq 'every month' ) { $time_lines[-2] = "every day"; pop @time_lines; } } else { # Ugh, there's some restriction on weekdays. # Translate the DOW-expression my $expression; my @hunks; foreach my $bit (@{$bits[4]}) { if(@$bit == 1) { push @hunks, $num2dow_long{$bit->[0]} || "DOW_$bit->[0]??"; } elsif(@$bit == 2) { if($bit->[0] eq '*') { # it's like */3 #push @hunks, sprintf "every %s day of the week", freq($bit->[1]); # the above was ambiguous -- "every third day of the week" # sounds synonymous with just "3" if($bit->[1] eq 2) { # common and unambiguous case. push @hunks, "every other day of the week"; } else { # rare cases: N > 2 push @hunks, "every $bit->[1] days of the week"; # sounds clunky, but it's a clunky concept } } else { # it's like "7-9" push @hunks, sprintf "%s through %s", $num2dow_long{$bit->[0]} || "DOW_$bit->[0]??", $num2dow_long{$bit->[1]} || "DOW_$bit->[1]??", } } elsif(@$bit == 3) { # "7-20/2" push @hunks, sprintf "every %s %s from %s through %s", ordinate_soft($bit->[2]), #$bit->[2], 'day', #$bit->[2] == 1 ? 'days' : 'days', $num2dow_long{$bit->[0]} || "DOW_$bit->[0]??", $num2dow_long{$bit->[1]} || "DOW_$bit->[1]??", } } $expression = conj_or(@hunks); # Now figure where to put it... # if($time_lines[-2] eq "every day of") { # Unrestricted day-of-month, hooray. # if($time_lines[-1] eq 'every month') { # change it to "every Tuesday", killing the "of every month". $time_lines[-2] = "every $expression"; $time_lines[-2] =~ s/every every /every /g; pop @time_lines; } else { # change it to "every Tuesday in" $time_lines[-2] = "every $expression in"; $time_lines[-2] =~ s/every every /every /g; } } else { # This is the messy case where there's a DOM and DOW # restriction # Was, wrongly: # $time_lines[-1] .= ','; # push @time_lines, "if it's also " . $expression; $time_lines[-2] .= " -- or every $expression in --"; # Yes, dashes look very strange, but then this is a very # rare case. $time_lines[-2] =~ s/every every /every /g; } } ####################################################################### } # TODO: change "3pm" -> "the 3pm hour" or something? $time_lines[-1] =~ s/ of$//s; return @time_lines; } ########################################################################### # Just utility routines below here. my %pretty_form; BEGIN { %pretty_form = ( '"' => '\"', '\\' => '\\\\', ); } sub esc { my $x = $_[0]; $x =~ #s<([^\x20\x21\x23\x27-\x3F\x41-\x5B\x5D-\x7E])> s<([\x00-\x1F"\\])> <$pretty_form{$1} || '\\x'.(unpack("H2",$1))>eg; return $x; } #-------------------------------------------------------------------------- # if($time_lines[-1] =~ m/every|from/) { # $time_lines[-1] =~ tr/,/;/; # s/ (and|or)\b/\; $1/g; # } sub conj_and { if(grep m/every|from/, @_) { # put in semicolons in the case of complex constituency return join('; and ', @_) if @_ < 2; my $last = pop @_; return join('; ', @_) . '; and ' . $last; } return join(' and ', @_) if @_ < 3; my $last = pop @_; return join(', ', @_) . ', and ' . $last; } sub conj_or { if(grep m/every|from/, @_) { # put in semicolons in the case of complex constituency return join('; or ', @_) if @_ < 2; my $last = pop @_; return join('; ', @_) . '; or ' . $last; } return join(' or ', @_) if @_ < 3; my $last = pop @_; return join(', ', @_) . ', or ' . $last; } #-------------------------------------------------------------------------- my %ordinations; BEGIN { # English-language overrides for common ordinals %ordinations = qw( 1 first 2 second 3 third 4 fourth 5 fifth 6 sixth 7 seventh 8 eighth 9 ninth 10 tenth ); # 11 eleventh 12 twelfth # 13 thirteenth 14 fourteen 15 fifteenth 16 sixteenth # 17 seventeenth 18 eighteenth 19 nineteenth 20 twentieth } sub ordsuf { return 'th' if not(defined($_[0])) or not( 0 + $_[0] ); # 'th' for undef, 0, or anything non-number. my $n = abs($_[0]); # Throw away the sign. return 'th' unless $n == int($n); # Best possible, I guess. $n %= 100; return 'th' if $n == 11 or $n == 12 or $n == 13; $n %= 10; return 'st' if $n == 1; return 'nd' if $n == 2; return 'rd' if $n == 3; return 'th'; } sub ordinate { my $i = $_[0] || 0; $ordinations{$i} || ($i . ordsuf($i)); } sub freq { # frequentive form. Like ordinal, except that 2 -> 'other' # (as in every other) my $i = $_[0] || 0; return 'other' if $i == 2; # special case $ordinations{$i} || ($i . ordsuf($i)); } sub ordinate_soft { my $i = $_[0] || 0; $i . ordsuf($i); } #-------------------------------------------------------------------------- sub percent_proc { # Translated literally from the C, cron/do_command.c. my($esc,$need_newline); my $out = ''; my $c; for(my $i = 0; $i < length($_[0]); $i++) { $c = substr($_[0],$i,1); if($esc) { $out .= "\\" unless $c eq '%'; } else { $c = "\n" if $c eq '%'; } unless($esc = ($c eq "\\")) { # For unescaped characters, $out .= $c; $need_newline = ($c ne "\n"); } } $out .= "\\" if $esc; $out .= "\n" if $need_newline; return $out; # I think this would do the same thing: # $x =~ s/((?:\\\\)+) | (\\\%) | (\%) /$3 ? "\n" : $2 ? '%' : $1/xeg; # But I don't want to think about it, and I need it to work just # as the original does. } #-------------------------------------------------------------------------- __END__ # Test data for crontab parsing: MAILTO=hulahoops@polygon.int 10 8 15 * * /bin/csh -c 'perl ~/thang.pl | mail -s hujambo root' 40 5 * * * df -k 50 6 * * 1 ls -l /tmp 1 2 * apr mOn foo foo=bar foo=bar foo = "bar" foo="bar" foo = 'bar' foo='bar' 1 2 3 4 7 Foo 1-20/3 * * * * Foo 1,2,3 * * * * Foo 1-9,15-30 * * * * Foo 1-9/3,15-30/4 * * * * Foo 1 2 3 jan mon stuff 1 2 3 4 mON stuff 1 2 3 jan 5 stuff @reboot xxstartuply foo @yearly xxx yearlijk%thig%hooboy @annually xxannuallijk heehoo @monthly xxx monthlijk @weekly xXxX weeklijk @daily xxxdaylijk @midnight xxxmidnightlijk @hourly xXxXhourlijk */3 * * * * stuff #End, really. examples/cron-stats.pl 0000644 00000030646 00000000000 0010766 0 ustar 00 #!/usr/bin/perl -w # # Generate stats of Cron messages sent to syslog # # # This script can be used to generate per-user and per-task # statistics in order to determine how much time do cron tasks take # and audit cron use. It might be useful to debug issues related # to cron tasks that take too much time to run, or overlap, or get executed # too fast.. # # Hint: In some systems, cron tasks might not properly use lockfiles to # prevent themselves from being run twice in a row and some "strange" # things might happen if the system is overloaded and a cron task takes # more than expected. And, no, this is not something that Cron should # prevent (for more information see Debian bug #194805). # # In order for this script to work # # together with the logging enhancements included # in Debian's vixie cron (3.0pl1-95) that make it possible to log # when do cron tasks end. # # # How to use: # - Modify /etc/init.d/cron so that the calls to cron pass the '-L 2' # argument # (Hint: change lines 27 and 47 so that they end with '-- -L 2 $LSBNAMES' # instead of '-- $LSBNAMES') # In Debian you need cron-3.0pl1-95 or later. # Note: future versions of cron might use an /etc/default/cron file # to provide arguments to cron. # # - Review /etc/syslog.conf to determine where does the output of the cron # facility goes to (might be /var/log/syslog together with other messages # but you can also configure syslog to store them at /var/log/cron.log) # # - Run the script (by default it will read input /var/log/syslog but you # can point to other file using the '-f' switch) and review the output # # - (Optionally) If you want to analyse log files for a long period # concatenate different log files, extract all CRON related entries # to a file and process that. # # You could do, for example, this: # # zcat -f /var/log/syslog* | grep CRON >cron-log ; \ # perl stats-cron.pl -f cron-log # # # This program is copyright 2006 by Javier Fernandez-Sanguino <jfs@debian.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # For more information please see # http://www.gnu.org/licenses/licenses.html#GPL # TODO: # - Print time internal of file analysis (from 'XXX' to 'XXX') based on the # first and the last log entry read. # # - Detect overlaped entries for the same task (start of an entry before the # previous one finished) # # - Make it possible to filter by users # # - Consider adapting to other log formats (other cron programs? Solaris?) by # separating analysis and extraction in the code and making it possible # to switch to different analysis methods. # Required modules eval 'use Parse::Syslog'; if ($@) { print STDERR "ERROR: Parse::Syslog not installed, please install this Perl library\n"; print STDERR "ERROR: (in Debian it is provided by the 'libparse-syslog-perl' package)\n"; exit 1; } use Parse::Syslog; use Getopt::Std; # Parse command line getopts('dhvf:'); $opt_d = 0 if ! defined ($opt_d) ; if ( $opt_h ) { print "Usage: $ARGV[0] [-dhv] [-f syslog file]"; exit 1; } # Note: in Debian's default syslog configuration log messages are # sent to /var/log/syslog but you might have edited it to use # /var/log/cron.log instead. Which, BTW, is more efficient because # we do not need to parse the syslog to extract only cron messages. my $LOGFILE = $opt_f || "/var/log/syslog"; # Sanity checks if ( ! -r $LOGFILE ) { print STDERR "ERROR: Cannot read logfile $LOGFILE"; } my $parser = Parse::Syslog->new( $LOGFILE); print STDERR "Starting parse of file $LOGFILE\n" if $opt_v; while(my $log = $parser->next) { if ( $log->{program} =~ /CRON/ ) { print STDERR "DEBUG: Found syslog entry: $log->{pid}\n" if $opt_d; # We extract the entries with the same text but with 'CMD' (start) # and 'END' (end) to check if it's the same we use the PID reported # by cron. if ($log->{text} =~ /CMD \(/) { my $pid; if ($log->{text} =~ /\(\[(\d+)\]/ ) { # Debian - with loglevel 8, the child process id is # reported $pid = $1; } else { # All the other cases, the process ID of the # child process is the same one as the log entry $pid = $log->{pid}; } add_entry($log->{timestamp},$log->{text},$pid, $log->{pid}) } if ($log->{text} =~ /END \(/) { my $pid; if ($log->{text} =~ /\(\[(\d+)\]/ ) { # Debian - with loglevel 8 $pid = $1; } else { # Other cases $pid = "UNKNOWN"; } clear_entry($log->{timestamp},$log->{text},$pid,$log->{pid}); } } } print STDERR "Finished parse of file $LOGFILE\n" if $opt_v; # Analysis, we find all entries with a start and stop timestamp # and print them generate_analysis(); # Stats: # - average time of execution of cron jobs # - cronjob which executed slowest # - cronjob which executed fastes # - cronjobs which start before they finish print_stats(); exit 0; sub find_user { # Extract a user from a cron entry my ($text) = @_; my $user = ""; if ( $text =~ /^\((.*?)\)/ ) { $user = $1; } return $user; } sub find_prg { # Extract a program from a cron entry my ($text) = @_; my $prg = ""; if ( $text =~ /(CM|EN)D\s*\((.*)\s*\)\s*$/ ) { $prg = $2; $prg =~ s/^\[\d+\]\s+//; # Strip off child process numbers # (Debian loglevel 8) } return $prg; } sub add_entry { # Get a syslog entry and add it to the table of entries for analysis my ($time, $text, $pid, $ppid) = @_; my ($user, $prg); print STDERR "DEBUG: Entering add_entry with @_\n" if $opt_d; $user = find_user($text); if ( ! defined ($user) ) { print STDERR "WARN: Cannot find user for cron job (pid $pid)\n"; return; } $prg = find_prg($text); if ( ! defined ($prg) ) { print STDERR "WARN: Cannot find program for cron job (pid $pid)\n"; return; } my $value = $pid."-".$time; print STDERR "DEBUG: Adding cronjob of user $user to list: $prg\n" if $opt_d; add_cron_entry($user, $prg, $value, $ppid); $cron_entries{$user}{$prg}{'count'}++; return; } sub clear_entry { # Get a syslog entry, find the start entry and add the timestamp difference # to the list of timestamps for that user # # There's two ways to match entries: # - (in Debian, using loglevel 8): By matching the child process ID # between the entries # - in all the other cases: By matching the process ID of the # END message (which belongs to the parent) with the process IF # of the CMD message (which belongs to the child) my ($timestamp, $text, $pid, $ppid) = @_; my ($user, $prg, $entry, $count); my @array; my @stack; print STDERR "DEBUG: Entering clear_entry with @_\n" if $opt_d; $user = find_user($text); if ( $user eq "" ) { print STDERR "WARN: Cannot find user for cron job (pid $pid)\n"; return; } $prg = find_prg($text); if ( $prg eq "" ) { print STDERR "WARN: Cannot find program for cron job (pid $pid)\n"; return; } if ( ! defined ( $cron_entries{$user}{$prg}{'item'} ) ) { print STDERR "WARN: End entry without start entry (pid $pid)\n"; return; } @array = split(':', $cron_entries{$user}{$prg}{'item'}); $count = $#array + 1 ; print STDERR "DEBUG: $count cron entries for $user, task '$prg'\n" if $opt_d; my $finish = 0; while ( $finish == 0 ) { $entry = pop @array; last if ! defined ($entry) ; # Pop all entries and look for one whose pid matches my ($spid, $stimestamp) = split ("-", $entry); print STDERR "DEBUG: Comparing entry $spid to $pid\n" if $opt_d; if ( ( $pid ne "UNKNOWN" && $pid == $spid ) || $ppid-$spid == 1 ) { my $timediff = $timestamp-$stimestamp; $timediff = 0 if $timediff < 0; print STDERR "DEBUG: Entries match, time difference of $timediff\n" if $opt_d ; $cron_entries{$user}{$prg}{'finished'}++; if (defined ( $cron_times{$user}{$prg} ) ) { $cron_times{$user}{$prg} = join(":", $cron_times{$user}{$prg}, $timediff); } else { $cron_times{$user}{$prg} = $timediff; } $finish = 1; } else { print STDERR "DEBUG: Pushing entry $spid into stack\n" if $opt_d; push @stack, $entry; } } # Push all remaining entries to the stack $cron_entries{$user}{$prg}{'item'}=""; $count = $#array + 1 ; if ($opt_d) { print STDERR "DEBUG: Restoring all entries (size: array $count"; $count = $#stack + 1 ; print STDERR ", stack $count)\n"; } print STDERR "DEBUG: Total finished tasks: $cron_entries{$user}{$prg}{'finished'} out of $cron_entries{$user}{$prg}{'count'}\n" if defined $cron_entries{$user}{$prg}{'finished'} && $opt_d; print STDERR "DEBUG: Unmatched now: $cron_entries{$user}{$prg}{'item'}\n" if $opt_d; while ( $entry = pop @array ) { add_cron_entry($user, $prg, $entry); } while ( $entry = pop @stack ) { add_cron_entry($user, $prg, $entry); } print STDERR "DEBUG: Finish execution of clear_entry\n" if $opt_d; return; } sub add_cron_entry { my ($user, $prg, $entry) = @_; if ( defined ($cron_entries{$user}{$prg}) && $cron_entries{$user}{$prg} ne "" ) { $cron_entries{$user}{$prg}{'item'} = join(":", $cron_entries{$user}{$prg}{'item'}, $entry); } else { $cron_entries{$user}{$prg}{'item'} = $entry; $cron_entries{$user}{$prg}{'count'} = 0; $cron_entries{$user}{$prg}{'finished'} = 0; $cron_entries{$user}{$prg}{'unmatched'} = 0 ; $cron_entries{$user}{$prg}{'average'} = 0 ; $cron_entries{$user}{$prg}{'minimum'} = 0 ; $cron_entries{$user}{$prg}{'maximum'} = 0; } } sub count_unmatched { my ($user, $prg) = @_; my ($count, @array); return 0 if ! defined ( $cron_entries{$user}{$prg}{'item'} ); @array = split(':', $cron_entries{$user}{$prg}{'item'}); $count = $#array + 1 ; return $count; } sub find_average { my ($user, $prg) = @_; my ($average, $count, $total, @array, $entry); $total = 0 ; return -1 if ! defined ( $cron_times{$user}{$prg} ); @array = split(':', $cron_times{$user}{$prg}); $count = $#array + 1 ; while ( defined ( $entry = pop @array ) ) { $total += $entry; } $average = $total / $count; return $average; } sub find_minimum { my ($user, $prg) = @_; my ($minimum, @array, $entry); $minimum = -1; return -1 if ! defined ( $cron_times{$user}{$prg} ); @array = split(':', $cron_times{$user}{$prg}); while ( defined ( $entry = pop @array ) ) { if ( $minimum == -1 ) { $minimum = $entry; } else { $minimum = $entry if $entry < $minimum; } } return $minimum; } sub find_maximum { my ($user, $prg) = @_; my ($maximum, @array); $maximum = -1; return -1 if ! defined ( $cron_times{$user}{$prg} ); @array = split(':', $cron_times{$user}{$prg}); while ( defined ( $entry = pop @array ) ) { if ( $maximum == -1 ) { $maximum = $entry ; } else { $maximum = $entry if $entry > $maximum; } } return $maximum; } sub generate_analysis { # For each user and program calculate the average time for the task my ($user, $prg); foreach $user (keys %cron_entries) { print STDERR "DEBUG: Calculating data for user '$user'\n" if $opt_d; foreach my $prg ( keys %{$cron_entries{$user}} ) { print STDERR "DEBUG: Calculating data for task '$prg'\n" if $opt_d; my $unmatched = count_unmatched($user, $prg); my $average = find_average($user, $prg); my $minimum = find_minimum($user, $prg); my $maximum = find_maximum($user, $prg); $cron_entries{$user}{$prg}{'unmatched'} = $unmatched; $cron_entries{$user}{$prg}{'average'} = $average; $cron_entries{$user}{$prg}{'minimum'} = $minimum; $cron_entries{$user}{$prg}{'maximum'} = $maximum; } } } sub print_stats { # Print information of cron statistics my ($user, $prg); foreach $user (keys %cron_entries) { print "Cron statistics for user '$user'\n"; foreach $prg ( keys %{$cron_entries{$user}} ) { print "\tTask: '$prg'\n"; print "\t\tDEBUG: $cron_times{$user}{$prg}\n" if $opt_d; print <<EOF \t\tTotal: $cron_entries{$user}{$prg}{'count'} \t\tFinished: $cron_entries{$user}{$prg}{'finished'} \t\tUnmatched: $cron_entries{$user}{$prg}{'unmatched'} \t\tAvg time: $cron_entries{$user}{$prg}{'average'} \t\tMin time: $cron_entries{$user}{$prg}{'minimum'} \t\tMax time: $cron_entries{$user}{$prg}{'maximum'} EOF } } } examples/cron-tasks-review.sh 0000644 00000012725 00000000000 0012251 0 ustar 00 #!/bin/bash # # Review the cron tasks defined in the system and warn the admin # if some of the files will not be run # # This program is copyright 2011 by Javier Fernandez-Sanguino <jfs@debian.org> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # For more information please see # http://www.gnu.org/licenses/licenses.html#GPL set -e # reset locale, just in case LC_ALL=C export LC_ALL PROGNAME=${0##*/} PROGVERSION=1.0 # Command line options SHORTOPTS=hvsi LONGOPTS=help,version,syslog,info set -- $(getopt -s bash -o $SHORTOPTS -l $LONGOPTS --n $PROGNAME -- "$@") version () { echo "$PROGNAME $PROGVERSION" echo "$PROGNAME is copyright © Javier Fernandez-Sanguino <jfs@debian.org>" echo "Released under the terms of the GPL version 2 or later" echo "This program is part of the cron package" } usage () { cat <<EOUSE Usage: $PROGNAME [-si] Reviews the directories used by cron and reports scripts that might not be run by cron due to problems in their naming or in their setup. You should run this program as root to prevent false positives. Options: -s -- Use syslog to report information -i -- Report also informational messages EOUSE } syslog="no" send_info="no" for opt in $@; do case $opt in -h|--help) usage; exit 0;; -v|--version) version; exit 0;; -s|--syslog) syslog="yes";; -i|--info) send_info="yes";; *) ;; esac done send_message () { level=$1 msg=$2 [ "$level" = "info" ] && [ "$send_info" = "no" ] && return if [ "$syslog" = "yes" ] ; then logger -p cron.$level -t CRON $msg else case $level in "warn") echo "WARN: $msg" >&2 ;; "info") echo "INFO: $msg" ;; esac fi } warn () { # Send a warning to the user file=$1 reason=$2 name=`basename $file` # Skip hidden files echo $name | grep -q -E '^\.' && return # Skip disabled files echo $name | grep -q -E '\.disabled' && return # TODO: Should we send warnings for '.old' or '.orig'? # Do not send a warning if the file is '.dpkg-old' or '.dpkg-dist' if ! echo $file | grep -q -E '\.dpkg-(old|dist)$' ; then send_message "warn" "The file $file will not be executed by cron: $reason" else send_message "info" "The file $file is a leftover from the Debian package manager" fi } check_results () { dir=$1 run_file=$2 exec_test=$3 # Now check the files we found and the ones that exist in the directory find $dir \( -type f -o -type l \) -printf '%p %l\n' | while read file pointer; do if ! grep -q "^$file$" $run_file; then [ -L "$file" ] && [ ! -e "$pointer" ] && \ warn $file "Points to an nonexistent location ($pointer)" && continue [ "$exec_test" = "yes" ] && [ ! -x "$file" ] && \ warn $file "Is not executable" && continue [ ! -r "$file" ] && [ "`id -u`" != "0" ] && \ warn $file "Cannot read the file to determine if it will be run ($PROGNAME is not running as root)" && continue [ ! -r "$file" ] && \ warn $file "File is unreadable" && continue warn $file "Does not conform to the run-parts convention" else # do additional checks for symlinks for files that *are* listed by run-parts if [ -L "$file" ] ; then # for symlinks: does the file exist? if [ ! -e "$pointer" ] ; then warn $file "Points to an nonexistent location ($pointer)" fi # for symlinks: is it owned by root? owner=`ls -l $pointer | awk '{print $3}'` if [ "$owner" != "root" ]; then warn $file "Is not owned by root" fi fi fi done } # Setup for the tests # First: check if we are using -l [ -r /etc/default/cron ] && . /etc/default/cron use_lsb="no" [ "$LSBNAMES" = "-l" ] && use_lsb="yes" echo $EXTRA_OPTS | grep -q -- '-l' && use_lsb="yes" # Set the options for run parts run_opts="" [ "$use_lsb" = "yes" ] && run_opts="--lsbsysinit" temp=`tempfile` || { echo "ERROR: Cannot create temporary file" >&2 ; exit 1; } trap "rm -f $temp" 0 1 2 3 13 15 # Now review the scripts, note that cron does not use run-parts to run these # so they are *not* required to be executables, just to conform with the # Step 1: Review /etc/cron.d run-parts $run_opts --list /etc/cron.d >$temp check_results /etc/cron.d $temp "no" # Step 2: Review /etc/cron.{hourly,daily,weekly,monthly} for interval in hourly daily weekly monthly; do testdir=/etc/cron.$interval run-parts $run_opts --test $testdir >$temp check_results $testdir $temp "yes" done exit 0
| ver. 1.4 |
Github
|
.
| PHP 8.3.30 | Generation time: 0.16 |
proxy
|
phpinfo
|
Settings