Please take our Survey
logo       

Choosing A Webhost:
A web hosting service is a type of Internet hosting service that allows individuals and organizations to provide their own website accessible via the World Wide Web. Web hosts are companies that provide space on a server they own for use by their clients as well as providing Internet connectivity, typically in a data center. Web hosts can also provide data center space and connectivity to the Internet for servers they do not own to be located in their data center, called colocation. more...

[SSI] openssi/logrotate .cvsignore,NONE,1.1.3.1.2.1 CHANGES,NONE,1.1.3.1.2.: msg#00332

Subject: [SSI] openssi/logrotate .cvsignore,NONE,1.1.3.1.2.1 CHANGES,NONE,1.1.3.1.2.1 COPYING,NONE,1.1.3.1.2.1 Makefile,NONE,1.1.3.1.2.1 README.HPUX,NONE,1.1.3.1.2.1 README.Solaris,NONE,1.1.3.1.2.1 basenames.c,NONE,1.1.3.1.2.1 basenames.h,NONE,1.1.3.1.2.1 config.c,NONE,1.1.3.1.2.1 config.h,NONE,1.1.3.1.2.1 log.c,NONE,1.1.3.1.2.1 log.h,NONE,1.1.3.1.2.1 logrotate.8,NONE,1.1.3.1.2.1 logrotate.c,NONE,1.1.3.1.2.1 logrotate.h,NONE,1.1.3.1.2.1 logrotate.spec,NONE,1.1.3.1.2.1
Update of /cvsroot/ssic-linux/openssi/logrotate
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8200/logrotate

Added Files:
      Tag: OPENSSI-RH
        .cvsignore CHANGES COPYING Makefile README.HPUX README.Solaris 
        basenames.c basenames.h config.c config.h log.c log.h 
        logrotate.8 logrotate.c logrotate.h logrotate.spec 
Log Message:
Added logrotate package to fix wtmp CDSL not being handled properly.

M openssi/Makefile
M openssi/install
M openssi/upgrade
        Changes to build and install logrotate package.
A openssi/logrotate/*
                 


--- NEW FILE: .cvsignore ---
logrotate
.depend

--- NEW FILE: CHANGES ---
3.5.4 -> 3.6:
        - See .spec file for changes

3.5.3 -> 3.5.4:
        - %defattr(-,root,root) in specfile

3.5.2 -> 3.5.3:
        - patch /tmp file race condition problem, use mkstemp;  
          Thanks go to Solar Designer <solar@xxxxxxxxxxxx>

3.5 -> 3.5.2:
        - added .swp and .rpmnew to default taboo list

3.5 -> 3.5.1:
        - handle state dates in the future a bit more sanely

3.4 -> 3.5:
        - multiple file names/patterns may be given for a single entry
        - fixed mistake in when logs were uncompressed before mailing

3.3.2 -> 3.4:
        - added sharedscripts/nosharedscripts
        - added simple testbed
        - quote filenames in state file to allow proper rotation of files
          with spaces in the name -- this changes the version number of
          the state file!
        - ignore white space at end of line

3.3.1 -> 3.3.2:
        - don't rotate lastlog

3.3 -> 3.3.1:
        - support gzipped man pages

3.2 -> 3.3:
        - added "mailfirst" and "maillast" flags (based on Tim Wall's patch)
        - documented "extension" flag
        - "rotate 0" gives proper script and mail behavior

3.1 -> 3.2:
        - create wtmp with correct perms

3.0 -> 3.1:
        - fixed small alloca()
        - added missingok flag
        - use popt to display usage message
        - handle /some/file { } in config file

2.9 -> 3.0
        - updates for glibc 2.1

2.8 -> 2.9:
        - fixed a bug parsing lines where { immediately follows the filename
        - allow log file patterns to be placed in double quotes, which  
          allows spaces in names
        - complain about missing log files (John Van Essen)
        
2.7 -> 2.8:
        - changes for glibc 2.1 (Cristian Gafton)

2.6 -> 2.7:
        - updated man page to include --force (Simon Mudd)
        - invoke scripts via /bin/sh rather then relying on /tmp execute
          semantics (Philip Guenther)
        - added "extension" option for forcing a file extension after rotation
          (Rob Hagopian)

2.5 -> 2.6:
        - added nodelaycompress flag (from Jos Vos)
        - added copytruncate, nocopytruncate flag (from Jos Vos)
        - removed umask handling; explicitly use fchmod() insteadmoved umask
        - added --force option (Simon Mudd)
        - moved /bin/mail to MAIL_COMMAND define (Simon Mudd)
        - fixed segv caused by overly long filenames
        - switched from getopt_long to popt

2.4 -> 2.5:
        - set the umask of the process to 0, letting open() create processes
          with the proper permissions
        - added delaycompress flag (from Jos Vos)
        - fixed how old logs are finally removed when an olddir is specified
          (Jos Vos)
        - added nomail option
        - added mail, nomail documentation to man page
        - added the tabooext directive
        - fixed problem in globbing

2.3 -> 2.4:
        - glob log names in config file
        - added ,v to taboo list
        - fixed bug w/ create parsing
        - use an int rather then a mode_t when parsing create entries as
          sscanf requires it

2.2 -> 2.3:
        - fill in all of last rotated structure (this probable isn't
          really necessary but it's a bit cleaner and will avoid future
          problems);
        - fixed .spec file

2.1 -> 2.2:
        - If a file is rotated and we have no state information for it,
          right out the current time.
        - Weekly rotation happens when the current weekday is less then
          the weekday of the last rotation or more then a week has
          elapsed between the last rotation and now
        - Monthly rotation happens when the current month is different
          from the last month or the current year is different from the
          last year
        - (these were contributed and suggested by Ronald Wahl)
        - added olddir/noolddir options
        - added ifempty/notifempty options
        - ignore nonnormal files when reading config files from a directory
        - (these were suggested and originally implemented by 
          Henning Schmiedehausen)
        - updated the man page to reflect these changes
        - made "make install" accept PREFIX argument
        - added .spec file to tarball

2.0.2 -> 2.1:
        - Don't output state information for logs that have never been
          rotated (better then 1900-1-0)
        - Accept 1900-1-0 as time 0
        
2.0.1 -> 2.0.2:
        - I have no idea :-(

2.0 -> 2.0.1:
        - ignore files in included directories which end with ~, .rpmorig, or
          .rpmsave
          

--- NEW FILE: COPYING ---
                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
                          675 Mass Ave, Cambridge, MA 02139, USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

                    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

                            NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) 19yy  <name of author>

    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., 675 Mass Ave, Cambridge, MA 02139, USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) 19yy name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Library General
Public License instead of this License.

--- NEW FILE: Makefile ---
VERSION = $(shell awk '/Version:/ { print $$2 }' logrotate.spec)
CVSROOT = $(shell cat CVS/Root)
CVSTAG = r$(subst .,-,$(VERSION))
OS_NAME = $(shell uname -s)
LFS = $(shell echo `getconf LFS_CFLAGS 2>/dev/null`)
CFLAGS = -Wall -D_GNU_SOURCE -D$(OS_NAME) -DVERSION=\"$(VERSION)\" 
$(RPM_OPT_FLAGS) $(LFS)
PROG = logrotate
MAN = logrotate.8
LOADLIBES = -lpopt

# HP-UX using GCC
ifeq ($(OS_NAME),HP-UX)
    ifeq ($(RPM_OPT_FLAGS),)
        RPM_OPT_FLAGS = -O2
    endif
    CC = gcc
    INSTALL = cpset
    ifeq ($(POPT_DIR),)
        POPT_DIR = /usr/local
    endif
    ifeq ($(HPLX_DIR),)
        HPLX_DIR = /usr/local/hplx
    endif
    LOADLIBES += -lhplx -L$(HPLX_DIR)/lib
    ifeq ($(BASEDIR),)
        BASEDIR = /usr/local
    endif
endif

# Solaris using gcc
ifeq ($(OS_NAME),SunOS)
    ifeq ($(RPM_OPT_FLAGS),)
        RPM_OPT_FLAGS = -O2
    endif
    CC = gcc
    INSTALL = install
    ifeq ($(BASEDIR),)
        BASEDIR = /usr/local
    endif
endif

# Red Hat Linux
ifeq ($(OS_NAME),Linux)
    INSTALL = install
    BASEDIR = /usr
endif

ifneq ($(POPT_DIR),)
    CFLAGS += -I$(POPT_DIR)
    LOADLIBES += -L$(POPT_DIR)
endif

ifneq ($(STATEFILE),)
    CFLAGS += -DSTATEFILE=\"$(STATEFILE)\"
endif

BINDIR = $(BASEDIR)/sbin
MANDIR = $(BASEDIR)/man

#--------------------------------------------------------------------------

OBJS = logrotate.o log.o config.o basenames.o
SOURCES = $(subst .o,.c,$(OBJS) $(LIBOBJS))

ifeq ($(RPM_OPT_FLAGS),)
CFLAGS += -g
LDFLAGS = -g
endif

ifeq (.depend,$(wildcard .depend))
TARGET=$(PROG)
else
TARGET=depend $(PROG)
endif

RCSVERSION = $(subst .,-,$(VERSION))

all: $(TARGET)

$(PROG): $(OBJS)

clean:
        rm -f $(OBJS) $(PROG) core* .depend

depend:
        $(CPP) $(CFLAGS) -M $(SOURCES) > .depend

.PHONY : test
test: $(TARGET)
        (cd test; ./test)

install:
        [ -d $(PREFIX)/$(BINDIR) ] || mkdir -p $(PREFIX)/$(BINDIR)
        [ -d $(PREFIX)/$(MANDIR) ] || mkdir -p $(PREFIX)/$(MANDIR)
        [ -d $(PREFIX)/$(MANDIR)/man8 ] || mkdir -p $(PREFIX)/$(MANDIR)/man8

        if [ "$(OS_NAME)" = HP-UX ]; then \
        $(INSTALL) $(PROG) $(PREFIX)/$(BINDIR) 0755 bin bin; \
        $(INSTALL) $(MAN) $(PREFIX)/$(MANDIR)/man`echo $(MAN) | sed "s/.*\.//"` 
0644 bin bin; \
        fi

        if [ "$(OS_NAME)" = Linux ]; then \
        $(INSTALL) -m 755 $(PROG) $(PREFIX)/$(BINDIR); \
        $(INSTALL) -m 644 $(MAN) $(PREFIX)/$(MANDIR)/man`echo $(MAN) | sed 
"s/.*\.//"`/$(MAN); \
        fi

co:
        co RCS/*,v
        (cd examples; co RCS/*,v)

cvstag:
        cvs tag -F $(CVSTAG) .

archive: cvstag
        @rm -rf /tmp/logrotate-$(VERSION) /tmp/logrotate
        @cd /tmp; cvs -d $(CVSROOT) export -r$(CVSTAG) logrotate; mv logrotate 
logrotate-$(VERSION)
        @cd /tmp/logrotate-$(VERSION)
        @cd /tmp; tar czSpf logrotate-$(VERSION).tar.gz logrotate-$(VERSION)
        @rm -rf /tmp/logrotate-$(VERSION)
        @cp /tmp/logrotate-$(VERSION).tar.gz .
        @rm -f /tmp/logrotate-$(VERSION).tar.gz
        @echo " "
        @echo "The final archive is ./logrotate-$(VERSION).tar.gz."

ifeq (.depend,$(wildcard .depend))
include .depend
endif

--- NEW FILE: README.HPUX ---
How to build and install logrotate on HP-UX 11.00 (these instructions should
also work on HP-UX 10.20):
 
1.  Obtain and install the following GNU packages for HP-UX:
        binutils 2.9.1
        gcc 2.95.2
        make 3.78.1
    I used the packages at the Software Porting and Archive Centre for HP-UX
    at http://hpux.cs.utah.edu/.

    Obtain and install the following GNU/Linux to HP-UX Porting package:
        libhplx library
    See http://devresource.hp.com/LPK/index.html for downloads.
    This library is needed to provide the ??? function.
 
2.  Obtain, build, and install popt 1.4 (there doesn't seem to be a build at
    the Porting Centre.)
    See ftp://ftp.rpm.org/pub/rpm/dist/rpm-4.0.x/popt-1.6.4.tar.gz
    Install it into the directory of your choice (i.e. 
    "./configure --prefix=/opt/popt").
 
3.  Build logrotate, telling it where to find popt and hplx installation. The
    POPT_DIR defaults to /usr/local and HPLX_DIR defaults to /usr/local/hplx:
        gmake POPT_DIR=/usr/local HPLX_DIR=/usr/local/hplx
 
4.  Install logrotate into your desired directory (BASEDIR defaults to
    /usr/local):
        gmake install BASEDIR=/usr/local
 
5.  Copy the configuration files into your desired location.
        cp examples/logrotate-default /etc/logrotate.conf
        mkdir /etc/logrotate.d

6.  Set up a cron job to run logrotate daily. See examples/logrotate.cron.
 
7.  I also recommend setting CLEAN_ADM=0 in /etc/rc.config.d/clean, and
    setting up an init script to use logrotate for this instead.  This way,
    logrotate manages all logfile pruning.
 
 
Questions, comments, abuse to:
    Paul D. Gear <citecpdg@xxxxxxxxxxxxxxxx>, <paulgear@xxxxxxxxxxx>
    Danial M. Howard <howadani@xxxxxxx>, <dmhoward@xxxxxxx>

--- NEW FILE: README.Solaris ---
Steps to build and install logrotate on Solaris 2.6 
 
1.  Obtain and install the following GNU packages from http://Sunfreeware.com:
        gcc 2.95.2
        make 3.80
        popt-1.6.3

2.  Build and install logrotate:
        gmake
        gmake install
 
OBS.: If you want to use the test script on Solaris 2.6, you'll need to have
      bash installed, adjust the path after the sha-bang (#!) properly and
      substitute the sintax $(...) for backticks `...` in all
      "test-config.?.in" files.

-- 
Fidelis Assis <fidelis@xxxxxxxxxxxxxxx>


--- NEW FILE: basenames.c ---
#include <stdlib.h>
#include <string.h>

#include "basenames.h"

/* Return NAME with any leading path stripped off.  */

char *ourBaseName(char *name) {
    char *base;

    base = strrchr(name, '/');
    return base ? base + 1 : name;
}

static void stripTrailingSlashes(char *path) {
    char * last;

    last = path + strlen(path) - 1;
    while (last > path && *last == '/')
        *last-- = '\0';
}

char * ourDirName(char * origname) {
    char * slash;
    char * name;
    int i = strlen(origname);

    name = strcpy(malloc(i + 1), origname);

    stripTrailingSlashes(name);

    slash = strrchr(name, '/');

    if (!slash) {
        /* No slash, must be current directory */
        free(name);
        /* strdup used, as the return value will be free()ed at some point */
        return strdup(".");
    } else {
        /* Remove any trailing slashes and final element. */
        while (slash > name && *slash == '/')
            --slash;
        slash[1] = '\0';
        return name;
    }
}

--- NEW FILE: basenames.h ---
#ifndef H_BASENAMES
#define H_BASENAMES

/* returns a pointer inside of name */
char * ourBaseName(char *name);
/* returns a malloc'd string which must be freed by the caller */
char * ourDirName(char * origname);

#endif

--- NEW FILE: config.c ---
#include <alloca.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <grp.h>
#include <popt.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <assert.h>

#include "basenames.h"
#include "log.h"
#include "logrotate.h"

#if !defined(GLOB_ABORTED) && defined(GLOB_ABEND)
#define GLOB_ABORTED GLOB_ABEND
#endif

#define REALLOC_STEP    10

#if defined(SunOS) && !defined(isblank)
#define isblank(c)      ( (c) == ' ' || (c) == '\t' ) ? 1 : 0
#endif

static char * defTabooExts[] = { ".rpmsave", ".rpmorig", "~", ",v",
                                 ".rpmnew", ".swp" };
static int defTabooCount = sizeof(defTabooExts) / sizeof(char *);

/* I shouldn't use globals here :-( */
static char ** tabooExts = NULL;
int tabooCount = 0;

static int readConfigFile(const char * configFile, logInfo * defConfig, 
                          logInfo ** logsPtr, int * numLogsPtr);
static int globerr(const char * pathname, int theerr);

static int isolateValue(const char * fileName, int lineNum, char * key, 
                        char ** startPtr, char ** endPtr) {
    char * chptr = *startPtr;

    while (isblank(*chptr)) chptr++;
    if (*chptr == '=') {
        chptr++;
        while (*chptr && isblank(*chptr)) chptr++;
    }

    if (*chptr == '\n') {
        message(MESS_ERROR, "%s:%d argument expected after %s\n", 
                fileName, lineNum, key);
        return 1;
    }

    *startPtr = chptr;

    while (*chptr != '\n') chptr++;

    while (isspace(*chptr)) chptr--;

    *endPtr = chptr + 1;

    return 0;
}

static char *readPath(const char *configFile, int lineNum, char *key,
                      char **startPtr) {
    char oldchar;
    char *endtag, *chptr;
    char *start = *startPtr;
    char *path;

    if (!isolateValue(configFile, lineNum, key, &start,
                      &endtag)) {
        oldchar = *endtag, *endtag = '\0';

        chptr = start;

        /* this is technically too restrictive -- let's see if anyone
           complains */
        while (*chptr && isprint(*chptr) && *chptr != ' ')
            chptr++;
        if (*chptr) {
            message(MESS_ERROR, "%s:%d bad %s path %s\n",
                    configFile, lineNum, key, start);
            return NULL;
        }
        path = strdup(start);

        *endtag = oldchar, start = endtag;

        *startPtr = start;

        return path;
    } else
        return NULL;
}

static char * readAddress(const char * configFile, int lineNum, char * key, 
                          char ** startPtr) {
    char oldchar;
    char * endtag, * chptr;
    char * start = *startPtr;
    char * address;

    if (!isolateValue(configFile, lineNum, key, &start, 
                      &endtag)) {
        oldchar = *endtag, *endtag = '\0';

        chptr = start;
        while (*chptr && isprint(*chptr) && *chptr != ' ') 
            chptr++;
        if (*chptr) {
            message(MESS_ERROR, "%s:%d bad %s address %s\n",
                    configFile, lineNum, key, start);
            return NULL;
        }

        address = strdup(start);

        *endtag = oldchar, start = endtag;

        *startPtr = start;

        return address;
    } else
        return NULL;
}

static int checkFile(const char * fname) {
    int i;

    /* Check if fname is '.' or '..'; if so, return false */
    if (fname[0] == '.' &&
        (!fname[1] || (fname[1] == '.' && !fname[2])))
        return 0;

    /* Check if fname is ending in a taboo-extension; if so, return
       false */
    for (i = 0; i < tabooCount; i++) {
        if (!strcmp(fname + strlen(fname) - strlen(tabooExts[i]),
            tabooExts[i])) {
            message(MESS_ERROR, "Ignoring %s, because of %s "
                    "ending\n", fname, tabooExts[i]);

            return 0;
        }
    }

      /* All checks have been passed; return true */
    return 1;
}

/* Used by qsort to sort filelist */
static int compar(const void *p, const void *q) {
        return  strcoll(*((char **)p), *((char **)q));
}

/* Free memory blocks pointed to by pointers in namelist and namelist itself */
static void free_namelist (char **namelist, int files_count)
{
    int i;
    for (i=0; i<files_count; ++i)
        free(namelist[i]);
    free(namelist);
}

int readConfigPath(const char * path, logInfo * defConfig, 
                          logInfo ** logsPtr, int * numLogsPtr) {
    struct stat sb;
    int here;

    if (!tabooExts) {
        tabooExts = malloc(sizeof(*tabooExts) * defTabooCount);
        memcpy(tabooExts, defTabooExts, sizeof(*tabooExts) * defTabooCount);
        tabooCount = defTabooCount;
    }

    if (stat(path, &sb)) {
        message(MESS_ERROR, "cannot stat %s: %s\n", path, strerror(errno));
        return 1;
    }

    if (S_ISDIR(sb.st_mode)) {
        char            **namelist, **p;
        struct dirent   *dp;
        int             files_count,i;
        DIR             *dirp;

        here = open(".", O_RDONLY);
        if (here < 0) {
            message(MESS_ERROR, "cannot open current directory: %s\n", 
                    strerror(errno));
            return 1;
        }

        if ( (dirp = opendir(path)) == NULL) {
            message(MESS_ERROR, "cannot open directory %s: %s\n", path,
                    strerror(errno));
            return 1;
        }
        files_count = 0;
        namelist = NULL;
        while ((dp = readdir(dirp)) != NULL) {
            if (checkFile(dp->d_name)) {
                /* Realloc memory for namelist array if necessary */
                if (files_count % REALLOC_STEP == 0) {
                    p = (char **) realloc(namelist, (files_count + 
REALLOC_STEP) * sizeof(char *));
                    if (p) {
                        namelist = p;
                        memset(namelist + files_count, '\0', REALLOC_STEP * 
sizeof(char *));
                    } else {
                        free_namelist(namelist, files_count);
                        message(MESS_ERROR, "cannot realloc: %s\n", 
strerror(errno));
                        return 1;
                    }
                }
                /* Alloc memory for file name */
                if ( (namelist[files_count] = (char *) malloc( 
strlen(dp->d_name) + 1)) ) {
                    strcpy(namelist[files_count], dp->d_name);
                    files_count++;
                } else {
                    free_namelist(namelist, files_count);
                    message(MESS_ERROR, "cannot realloc: %s\n", 
strerror(errno));
                    return 1;
                }
            }
        }
        closedir( dirp );

        if (files_count > 0) {
            qsort(namelist, files_count, sizeof(char *), compar);
        } else {
            return 0;
        }

        if (chdir(path)) {
            message(MESS_ERROR, "error in chdir(\"%s\"): %s\n", path,
                    strerror(errno));
            close(here);
            free_namelist(namelist, files_count);
            return 1;
        }

        for (i=0; i<files_count; ++i) {
          assert(namelist[i] != NULL);
          
          if (readConfigFile(namelist[i], defConfig, logsPtr, 
                             numLogsPtr)) {
            fchdir(here);
            close(here);
            free_namelist(namelist, files_count);
            return 1;
          }
        };

        fchdir(here);
        close(here);
        free_namelist(namelist, files_count);
    } else {
        return readConfigFile(path, defConfig, logsPtr, numLogsPtr);
    }

    return 0;
}

static int globerr(const char * pathname, int theerr) {
    message(MESS_ERROR, "error accessing %s: %s\n", pathname, 
            strerror(theerr));

    /* We want the glob operation to continue, so return 0 */
    return 1;
}

static int readConfigFile(const char * configFile, logInfo * defConfig, 
                          logInfo ** logsPtr, int * numLogsPtr) {
    int fd;
    char * buf, * endtag;
    char oldchar, foo;
    int length;
    int lineNum = 1;
    int multiplier;
    int i, j, k;
    char * scriptStart = NULL;
    char ** scriptDest = NULL;
    logInfo * newlog = defConfig;
    char * start, * chptr;
    char * dirName;
    struct group * group;
    struct passwd * pw;
    int rc;
    char createOwner[200], createGroup[200];
    int createMode;
    struct stat sb, sb2;
    glob_t globResult;
    const char ** argv;
    int argc, argNum;

    /* FIXME: createOwner and createGroup probably shouldn't be fixed
       length arrays -- of course, if we aren't run setuid it doesn't
       matter much */

    fd = open(configFile, O_RDONLY);
    if (fd < 0) {
        message(MESS_ERROR, "failed to open config file %s: %s\n",
                configFile, strerror(errno));
        return 1;
    }

    if (fstat(fd, &sb)) {
        message(MESS_ERROR, "fstat of %s failed: %s\n", configFile,
                strerror(errno));
        close(fd);
        return 1;
    }
    if (!S_ISREG(sb.st_mode)) {
        message(MESS_DEBUG, "Ignoring %s because it's not a regular file.\n",
                configFile);
        close(fd);
        return 0;
    }

    length = lseek(fd, 0, SEEK_END);
    lseek(fd, 0, SEEK_SET);

    buf = alloca(length + 2);
    if (!buf) {
        message(MESS_ERROR, "alloca() of %d bytes failed\n", length);
        close(fd);
        return 1;
    }

    if (read(fd, buf, length) != length) {
        message(MESS_ERROR, "failed to read %s: %s\n", configFile, 
                strerror(errno));
        close(fd);
        return 1;
    }

    close(fd);

    /* knowing the buffer ends with a newline makes things (a bit) cleaner */
    buf[length + 1] = '\0';
    buf[length] = '\n';

    message(MESS_DEBUG, "reading config file %s\n", configFile);

    start = buf;
    while (*start) {
        while (isblank(*start) && (*start)) start++;
        if (*start == '#') {
            while (*start != '\n') start++;
        }

        if (*start == '\n') {
            start++;
            lineNum++;
            continue;
        }

        if (scriptStart) {
            if (!strncmp(start, "endscript", 9)) {
                chptr = start + 9;
                while (isblank(*chptr)) chptr++;
                if (*chptr == '\n') {
                    endtag = start;
                    while (*endtag != '\n') endtag--;
                    endtag++;
                    *scriptDest = malloc(endtag - scriptStart + 1);
                    strncpy(*scriptDest, scriptStart, endtag - scriptStart);
                    (*scriptDest)[endtag - scriptStart] = '\0';
                    start = chptr + 1;
                    lineNum++;

                    scriptDest = NULL;
                    scriptStart = NULL;
                }
            } 

            if (scriptStart) {
                while (*start != '\n') start++;
                lineNum++;
                start++;
            }
        } else if (isalpha(*start)) {
            endtag = start;
            while (isalpha(*endtag)) endtag++;
            oldchar = *endtag;
            *endtag = '\0';

            if (!strcmp(start, "compress")) {
                newlog->flags |= LOG_FLAG_COMPRESS;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "nocompress")) {
                newlog->flags &= ~LOG_FLAG_COMPRESS;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "delaycompress")) {
                newlog->flags |= LOG_FLAG_DELAYCOMPRESS;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "nodelaycompress")) {
                newlog->flags &= ~LOG_FLAG_DELAYCOMPRESS;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "sharedscripts")) {
                newlog->flags |= LOG_FLAG_SHAREDSCRIPTS;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "nosharedscripts")) {
                newlog->flags &= ~LOG_FLAG_SHAREDSCRIPTS;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "copytruncate")) {
                newlog->flags |= LOG_FLAG_COPYTRUNCATE;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "nocopytruncate")) {
                newlog->flags &= ~LOG_FLAG_COPYTRUNCATE;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "copy")) {
                newlog->flags |= LOG_FLAG_COPY;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "nocopy")) {
                newlog->flags &= ~LOG_FLAG_COPY;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "ifempty")) {
                newlog->flags |= LOG_FLAG_IFEMPTY;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "notifempty")) {
                newlog->flags &= ~LOG_FLAG_IFEMPTY;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "noolddir")) {
                newlog->oldDir = NULL;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "mailfirst")) {
                newlog->flags |= LOG_FLAG_MAILFIRST;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "maillast")) {
                newlog->flags &= ~LOG_FLAG_MAILFIRST;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "create")) {
                *endtag = oldchar, start = endtag;

                endtag = start;
                while (*endtag != '\n') endtag++;
                while (isspace(*endtag)) endtag--;
                endtag++;
                oldchar = *endtag, *endtag = '\0';
                
                rc = sscanf(start, "%o %s %s%c", &createMode, 
                                createOwner, createGroup, &foo);
                if (rc == 4) {
                    message(MESS_ERROR, "%s:%d extra arguments for "
                            "create\n", configFile, lineNum);
                    return 1;
                }

                if (rc > 0)
                    newlog->createMode = createMode;
                
                if (rc > 1) {
                    pw = getpwnam(createOwner);
                    if (!pw) {
                        message(MESS_ERROR, "%s:%d unknown user '%s'\n", 
                                configFile, lineNum, createOwner);
                        return 1;
                    } 
                    newlog->createUid = pw->pw_uid;
                    endpwent();
                } 
                if (rc > 2) {
                    group = getgrnam(createGroup);
                    if (!group) {
                        message(MESS_ERROR, "%s:%d unknown group '%s'\n", 
                                configFile, lineNum, createGroup);
                        return 1;
                    } 
                    newlog->createGid = group->gr_gid;
                    endgrent();
                } 

                newlog->flags |= LOG_FLAG_CREATE;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "nocreate")) {
                newlog->flags &= ~LOG_FLAG_CREATE;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "size")) {
                *endtag = oldchar, start = endtag;

                if (!isolateValue(configFile, lineNum, "size", &start, 
                                  &endtag)) {
                    oldchar = *endtag, *endtag = '\0';

                    length = strlen(start) - 1;
                    if (start[length] == 'k') {
                        start[length] = '\0';
                        multiplier = 1024;
                    } else if (start[length] == 'M') {
                        start[length] = '\0';
                        multiplier = 1024 * 1024;
                    } else if (start[length] == 'G') {
                        start[length] = '\0';
                        multiplier = 1024 * 1024 * 1024;
                    } else if (!isdigit(start[length])) {
                        message(MESS_ERROR, "%s:%d unknown unit '%c'\n",
                                    configFile, lineNum, start[length]);
                        return 1;
                    } else {
                        multiplier = 1;
                    }

                    newlog->threshhold = multiplier * strtoul(start, &chptr, 0);
                    if (*chptr) {
                        message(MESS_ERROR, "%s:%d bad size '%s'\n",
                                    configFile, lineNum, start);
                        return 1;
                    }

                    newlog->criterium = ROT_SIZE;

                    *endtag = oldchar, start = endtag;
                }
#if 0   /* this seems like such a good idea :-( */
            } else if (!strcmp(start, "days")) {
                *endtag = oldchar, start = endtag;

                if (!isolateValue(configFile, lineNum, "size", &start, 
                                  &endtag)) {
                    oldchar = *endtag, *endtag = '\0';

                    newlog->threshhold = strtoul(start, &chptr, 0);
                    if (*chptr) {
                        message(MESS_ERROR, "%s:%d bad number of days'%s'\n",
                                    configFile, lineNum, start);
                        return 1;
                    }

                    newlog->criterium = ROT_DAYS;

                    *endtag = oldchar, start = endtag;
                }
#endif
            } else if (!strcmp(start, "daily")) {
                *endtag = oldchar, start = endtag;

                newlog->criterium = ROT_DAYS;
                newlog->threshhold = 1;
            } else if (!strcmp(start, "monthly")) {
                *endtag = oldchar, start = endtag;

                newlog->criterium = ROT_MONTHLY;
            } else if (!strcmp(start, "weekly")) {
                *endtag = oldchar, start = endtag;

                newlog->criterium = ROT_WEEKLY;
            } else if (!strcmp(start, "rotate")) {
                *endtag = oldchar, start = endtag;

                if (!isolateValue(configFile, lineNum, "rotate count", &start, 
                                  &endtag)) {
                    oldchar = *endtag, *endtag = '\0';

                    newlog->rotateCount = strtoul(start, &chptr, 0);
                    if (*chptr || newlog->rotateCount < 0) {
                        message(MESS_ERROR, "%s:%d bad rotation count '%s'\n",
                                    configFile, lineNum, start);
                        return 1;
                    }
                    *endtag = oldchar, start = endtag;
                }
            } else if (!strcmp(start, "start")) {
                *endtag = oldchar, start = endtag;

                if (!isolateValue(configFile, lineNum, "start count", &start,
                                  &endtag)) {
                    oldchar = *endtag, *endtag = '\0';

                    newlog->logStart = strtoul(start, &chptr, 0);
                    if (*chptr || newlog->logStart < 0) {
                      message(MESS_ERROR, "%s:%d bad start count '%s'\n",
                              configFile, lineNum, start);
                      return 1;
                    }
                    *endtag = oldchar, start = endtag;
                }
            } else if (!strcmp(start, "errors")) {
                message(MESS_DEBUG, "%s: %d: the errors directive is deprecated 
and no longer used.\n",
                        configFile, lineNum);
            } else if (!strcmp(start, "mail")) {
                *endtag = oldchar, start = endtag;
                if (!(newlog->logAddress = readAddress(configFile, lineNum, 
                                                        "mail", &start))) {
                    return 1;
                }
            } else if (!strcmp(start, "nomail")) {
                /* hmmm, we could lose memory here, but not much */
                newlog->logAddress = NULL;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "missingok")) {
                newlog->flags |= LOG_FLAG_MISSINGOK;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "nomissingok")) {
                newlog->flags &= ~LOG_FLAG_MISSINGOK;

                *endtag = oldchar, start = endtag;
            } else if (!strcmp(start, "prerotate")) {
                *endtag = oldchar, start = endtag;

                scriptStart = start;
                scriptDest = &newlog->pre;

                while (*start != '\n') start++;
            } else if (!strcmp(start, "firstaction")) {
                *endtag = oldchar, start = endtag;

                scriptStart = start;
                scriptDest = &newlog->first;

                while (*start != '\n') start++;
            } else if (!strcmp(start, "postrotate")) {
                *endtag = oldchar, start = endtag;

                scriptStart = start;
                scriptDest = &newlog->post;

                while (*start != '\n') start++;
            } else if (!strcmp(start, "lastaction")) {
                *endtag = oldchar, start = endtag;

                scriptStart = start;
                scriptDest = &newlog->last;

                while (*start != '\n') start++;
            } else if (!strcmp(start, "tabooext")) {
                if (newlog != defConfig) {
                    message(MESS_ERROR, "%s:%d tabooext may not appear inside "
                            "of log file definition\n", configFile, lineNum);
                    return 1;
                }

                *endtag = oldchar, start = endtag;
                if (!isolateValue(configFile, lineNum, "size", &start, 
                                  &endtag)) {
                    oldchar = *endtag, *endtag = '\0';

                    if (*start == '+') {
                        start++;
                        while (isspace(*start) && *start) start++;
                    } else {
                        free(tabooExts);
                        tabooCount = 0;
                        tabooExts = malloc(1);
                    }

                    while (*start) {
                        chptr = start;
                        while (!isspace(*chptr) && *chptr != ',' && *chptr)
                            chptr++;

                        tabooExts = realloc(tabooExts, sizeof(*tabooExts) * 
                                                (tabooCount + 1));
                        /* this is a memory leak if the list gets reset */
                        tabooExts[tabooCount] = malloc(chptr - start + 1);
                        strncpy(tabooExts[tabooCount], start, chptr - start);
                        tabooExts[tabooCount][chptr - start] = '\0';
                        tabooCount++;

                        start = chptr;
                        if (*start == ',') start++;
                        while (isspace(*start) && *start) start++;
                    }

                    *endtag = oldchar, start = endtag;
                }
            } else if (!strcmp(start, "include")) {
                if (newlog != defConfig) {
                    message(MESS_ERROR, "%s:%d include may not appear inside "
                            "of log file definition\n", configFile, lineNum);
                    return 1;
                }

                *endtag = oldchar, start = endtag;
                if (!isolateValue(configFile, lineNum, "size", &start, 
                                  &endtag)) {
                    oldchar = *endtag, *endtag = '\0';

                    message(MESS_DEBUG, "including %s\n", start);

                    if (readConfigPath(start, defConfig, logsPtr, 
                                       numLogsPtr))
                        return 1;

                    *endtag = oldchar, start = endtag;
                }
            } else if (!strcmp(start, "olddir")) {
                *endtag = oldchar, start = endtag;
                if (!(newlog->oldDir = readPath(configFile, lineNum,
                                                "olddir", &start))) {
                    return 1;
                }

#if 0
                if (stat(newlog->oldDir, &sb)) {
                    message(MESS_ERROR, "%s:%d error verifying olddir "
                                "path %s: %s\n", configFile, lineNum, 
                                newlog->oldDir, strerror(errno));
                    return 1;
                }

                if (!S_ISDIR(sb.st_mode)) {
                    message(MESS_ERROR, "%s:%d olddir path %s is not a "
                                "directory\n", configFile, lineNum, 
                                newlog->oldDir);
                    return 1;
                }
#endif

                message(MESS_DEBUG, "olddir is now %s\n", newlog->oldDir);
            } else if (!strcmp(start, "extension")) {
                *endtag = oldchar, start = endtag;

                if (!isolateValue(configFile, lineNum, "extension name", 
&start, 
                                  &endtag)) {
                    oldchar = *endtag, *endtag = '\0';

                    newlog->extension = strdup(start);

                    *endtag = oldchar, start = endtag;
                }

                message(MESS_DEBUG, "extension is now %s\n", newlog->extension);

            } else if (!strcmp(start, "compresscmd")) {
                *endtag = oldchar, start = endtag;
                if (!(newlog->compress_prog = readPath(configFile, lineNum, 
"compress", &start))) {
                    return 1;
                }

                if (access(newlog->compress_prog, X_OK)) {
                    message(MESS_ERROR, "%s:%d compression program %s is not an 
executable file\n", configFile, lineNum, 
                                newlog->compress_prog);
                    return 1;
                }

                message(MESS_DEBUG, "compress_prog is now %s\n", 
newlog->compress_prog);

            } else if (!strcmp(start, "uncompresscmd")) {
                *endtag = oldchar, start = endtag;
                if (!(newlog->uncompress_prog = readPath(configFile, lineNum, 
"uncompress", &start))) {
                    return 1;
                }

                if (access(newlog->uncompress_prog, X_OK)) {
                    message(MESS_ERROR, "%s:%d uncompression program %s is not 
an executable file\n", configFile, lineNum, 
                                newlog->uncompress_prog);
                    return 1;
                }

                message(MESS_DEBUG, "uncompress_prog is now %s\n", 
newlog->uncompress_prog);

            } else if (!strcmp(start, "compressoptions")) {
                *endtag = oldchar, start = endtag;
                if (!(newlog->compress_options = readPath(configFile, lineNum, 
"compressoptions", &start))) {
                    return 1;
                }

                message(MESS_DEBUG, "compress_options is now %s\n", 
newlog->compress_options);

            } else if (!strcmp(start, "compressext")) {
                *endtag = oldchar, start = endtag;
                if (!(newlog->compress_ext = readPath(configFile, lineNum, 
"compress-ext", &start))) {
                    return 1;
                }

                message(MESS_DEBUG, "compress_ext is now %s\n", 
newlog->compress_ext);
            } else {
                message(MESS_ERROR, "%s:%d unknown option '%s' "
                            "-- ignoring line\n", configFile, lineNum, start);

                *endtag = oldchar, start = endtag;
            }

            while (isblank(*start)) start++;

            if (*start != '\n') {
                message(MESS_ERROR, "%s:%d unexpected text\n", configFile,
                            lineNum);
                while (*start != '\n') start++;
            }

            lineNum++;
            start++;
        } else if (*start == '/' || *start == '"' || *start == '\'') {
            if (newlog != defConfig) {
                message(MESS_ERROR, "%s:%d unexpected log filename\n", 
                        configFile, lineNum);
                return 1;
            }

            (*numLogsPtr)++;
            *logsPtr = realloc(*logsPtr, sizeof(**logsPtr) * *numLogsPtr);
            newlog = *logsPtr + *numLogsPtr - 1;
            memcpy(newlog, defConfig, sizeof(*newlog));

            endtag = start;
            while (*endtag != '{' && *endtag != '\0') endtag++;
            if (*endtag != '{') {
                message(MESS_ERROR, "%s:%d missing end of line\n",
                        configFile, lineNum);
            }
            *endtag = '\0';

            if (poptParseArgvString(start, &argc, &argv)) {
                message(MESS_ERROR, "%s:%d error parsing filename\n",
                        configFile, lineNum);
                return 1;
            } else if (argc < 1) {
                message(MESS_ERROR, "%s:%d { expected after log file name(s)\n",
                        configFile, lineNum);
                return 1;
            }

            /* XXX this leaks the result of the glob <shrug> */
            newlog->files = NULL;
            newlog->numFiles = 0;
            for (argNum = 0; argNum < argc; argNum++) {
                rc = glob(argv[argNum], GLOB_NOCHECK, globerr, &globResult);
                if (rc == GLOB_ABORTED) {
                    message(MESS_ERROR, "%s:%d glob failed for %s\n",
                            configFile, lineNum, argv[argNum]);
                    return 1;
                }

                newlog->files = realloc(newlog->files, sizeof(*newlog->files) * 
                                   (newlog->numFiles + globResult.gl_pathc));

                for (i = 0; i < globResult.gl_pathc; i++) {
                    /* if we glob directories we can get false matches */
                    if (!lstat(globResult.gl_pathv[i], &sb) && 
                                    S_ISDIR(sb.st_mode)) 
                        continue;

                    for (j = 0; j < *numLogsPtr - 1; j++) {
                        for (k = 0; k < (*logsPtr)[j].numFiles; k++) {
                            if (!strcmp((*logsPtr)[j].files[k], 
                                        globResult.gl_pathv[i])) {
                                message(MESS_ERROR, 
                                        "%s:%d duplicate log entry for %s\n",
                                        configFile, lineNum, 
                                        globResult.gl_pathv[i]);
                                return 1;
                            }
                        }
                    }

                    newlog->files[newlog->numFiles] = 
                            globResult.gl_pathv[i];
                    newlog->numFiles++;
                }
            }

            newlog->pattern = strdup(start);

            message(MESS_DEBUG, "reading config info for %s\n", start);

            free(argv);

            start = endtag + 1;
        } else if (*start == '}') {
            if (newlog == defConfig) {
                message(MESS_ERROR, "%s:%d unxpected }\n", configFile, lineNum);
                return 1;
            }

            if (newlog->oldDir) {
                for (i = 0; i < newlog->numFiles; i++) {
                    char *ld;
                    dirName = ourDirName(newlog->files[i]);
                    if (stat(dirName, &sb2)) {
                        message(MESS_ERROR, "%s:%d error verifying log file "
                                    "path %s: %s\n", configFile, lineNum, 
                                    dirName, strerror(errno));
                        free(dirName);
                        return 1;
                    }
                    ld = alloca(strlen(dirName) + strlen(newlog->oldDir) + 2);
                    sprintf(ld, "%s/%s", dirName, newlog->oldDir);
                    free(dirName);

                    if(newlog->oldDir[0] != '/') dirName = ld;
                    else dirName = newlog->oldDir;
                    if(stat(dirName, &sb)) {
                        message(MESS_ERROR, "%s:%d error verifying olddir "
                                "path %s: %s\n", configFile, lineNum,
                                dirName, strerror(errno));
                                return 1;
                    }

                    if (sb.st_dev != sb2.st_dev) {
                        message(MESS_ERROR, "%s:%d olddir %s and log file %s "
                                    "are on different devices\n", configFile,
                                    lineNum, newlog->oldDir, newlog->files[i]);
                        return 1;
                    }
                }
            }

            newlog = defConfig;

            start++;
            while (isblank(*start)) start++;

            if (*start != '\n') {
                message(MESS_ERROR, "%s:%d, unexpected text after {\n",
                        configFile, lineNum);
            }
        } else {
            message(MESS_ERROR, "%s:%d lines must begin with a keyword "
                        "or a filename (possibly in double quotes)\n", 
                        configFile, lineNum);

            while (*start != '\n') start++;
            lineNum++;
            start++;
        }
    }

    if(scriptStart) {
      message(MESS_ERROR, "%s:prerotate or postrotate without endscript\n",
              configFile);
      return 1;
    }

    return 0;
}


--- NEW FILE: config.h ---
/*
 * OS-specific definitions
 */

#ifdef __hpux
    #define DEFAULT_MAIL_COMMAND "/usr/bin/mailx -s"
    #define COMPRESS_COMMAND "/usr/contrib/bin/gzip"
    #define UNCOMPRESS_COMMAND " /usr/contrib/bin/gunzip"
    #define STATEFILE "/var/run/logrotate.status"
#endif

#ifdef SunOS
    #define DEFAULT_MAIL_COMMAND "/usr/bin/mailx -s"
    #define COMPRESS_COMMAND "/usr/local/bin/gzip"
    #define UNCOMPRESS_COMMAND "/usr/local/bin/gunzip"
    #define STATEFILE "/var/log/logrotate.status"
#endif

/*
 * Default settings for Linux - leave these last.
 */
#ifndef DEFAULT_MAIL_COMMAND
    #define DEFAULT_MAIL_COMMAND "/bin/mail -s"
#endif

#ifndef COMPRESS_COMMAND
    #define COMPRESS_COMMAND "/bin/gzip"
#endif

#ifndef COMPRESS_OPTIONS
    #define COMPRESS_OPTIONS ""
#endif

#ifndef COMPRESS_EXT
    #define COMPRESS_EXT ".gz"
#endif

#ifndef UNCOMPRESS_COMMAND
    #define UNCOMPRESS_COMMAND "/bin/gunzip"
#endif

#ifndef STATEFILE
    #define STATEFILE "/var/lib/logrotate.status"
#endif


--- NEW FILE: log.c ---
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>

#include "log.h"

int logLevel = MESS_DEBUG;
static FILE * errorFile =  NULL;
static FILE * messageFile = NULL;
int flags = 0;

void logSetLevel(int level) {
    logLevel = level;
}

void logSetErrorFile(FILE * f) {
    errorFile = f;
}

void logSetMessageFile(FILE * f) {
    messageFile = f;
}

void logSetFlags(int newFlags) {
    flags |= newFlags;
}

void logClearFlags(int newFlags) {
    flags &= ~newFlags;
}

#if 0
void log(int fd, char * format, ...) {
    int i = 0;
    char * buf = NULL;
    va_list args;
    int size;

    va_start(args, format);

    do {
        i += 1000;
        if (buf) free(buf);
        buf = malloc(i);
        size = vsnprintf(buf, i, format, args);
    } while (size >= i);

    write(fd, buf, size);

    free(buf);

    va_end(args);
}
#endif

void message(int level, char * format, ...) {
    va_list args;
    FILE * where = NULL;
    int showTime = 0;

    if (errorFile == NULL)
        errorFile = stderr;
    if (messageFile == NULL)
        messageFile = stderr;
    where = errorFile;
    
    if (level >= logLevel) {
        va_start(args, format);

        switch (level) {
          case MESS_DEBUG:
            where = messageFile;
            showTime = 1;
            break;

          case MESS_NORMAL:
          case MESS_VERBOSE:
            where = messageFile;
            break;

          default:
            if (flags & LOG_TIMES) 
                fprintf(where, "%ld: ", (long) time(NULL));
            fprintf(errorFile, "error: ");
            break;
        }

        if (showTime && (flags & LOG_TIMES)) {
            fprintf(where, "%ld:", (long) time(NULL));
        }

        vfprintf(where, format, args);
        fflush(where);

        va_end(args);

        if (level == MESS_FATAL) exit(1);
    }
}


--- NEW FILE: log.h ---
#ifndef H_LOG
#define H_LOG

#include <stdio.h>

#define MESS_REALDEBUG  1
#define MESS_DEBUG      2
#define MESS_VERBOSE    3
#define MESS_NORMAL     4
#define MESS_ERROR      5
#define MESS_FATAL      6

#define LOG_TIMES       (1 << 0)

void message(int level, char * format, ...)
#ifdef __GNUC__
       __attribute__ ((format (printf, 2, 3)));
#else
       ;
#endif
#if 0
void log(int fd, char * format, ...);
#endif
void logSetErrorFile(FILE * f);
void logSetMessageFile(FILE * f);
void logSetFlags(int flags);
void logClearFlags(int flags);
void logSetLevel(int level);

#endif

--- NEW FILE: logrotate.8 ---
.TH LOGROTATE 8 "Wed Nov 5 2002" "Red Hat Linux" "System Administrator's Manual"
.UC 4
.SH NAME
logrotate \- rotates, compresses, and mails system logs
.SH SYNOPSIS
\fBlogrotate\fR [-dv] [-f|--force] [-s|--state \fIfile\fR] \fIconfig_file\fR+
.SH DESCRIPTION
\fBlogrotate\fR is designed to ease administration of systems that generate
large numbers of log files.  It allows automatic rotation, compression, 
removal, and mailing of log files.  Each log file may be handled daily,
weekly, monthly, or when it grows too large.
.P
Normally, \fBlogrotate\fR is run as a daily cron job.  It will not modify
a log multiple times in one day unless the criterium for that log is
based on the log's size and \fBlogrotate\fR is being run multiple times
each day, or unless the \fB-f\fR or \fB-force\fR option is used. 
.P
Any number of config files may be given on the command line. Later config
files may override the options given in earlier files, so the order
in which the \fBlogrotate\fR config files are listed in is important.
Normally, a single config file which includes any other config files
which are needed should be used.  See below for more information on how
to use the \fIinclude\fR directive to accomplish this.  If a directory
is given on the command line, every file in that directory is used as
a config file.
.P
If no command line arguments are given, \fBlogrotate\fR will print
version and copyright information, along with a short usage summary.  If
any errors occur while rotating logs, \fBlogrotate\fR will exit with
non-zero status.

.SH OPTIONS
.TP
\fB-d\fR
Turns on debug mode and implies \fB-v\fR.  In debug mode, no changes will
be made to the logs or to the \fBlogrotate\fR state file.

.TP
\fB-f, -\-force\fR
Tells \fBlogrotate\fR to force the rotation, even if it doesn't think
this is necessary.  Sometimes this is useful after adding new entries to
\fBlogrotate\fR, or if old log files have been removed by hand, as the
new files will be created, and logging will continue correctly.

.TP
\fB-m, -\-mail <command>\fR
Tells \fBlogrotate\fR which command to use when mailing logs. This
command should accept two arguments: 1) the subject of the message, and
2) the recipient. The command must then read a message on standard input
and mail it to the recipient. The default mail command is \fB/bin/mail
-s\fR.

.TP
\fB-s, -\-state <statefile>\fR
Tells \fBlogrotate\fR to use an alternate state file.  This is useful
if logrotate is being run as a different user for various sets of
log files.  The default state file is \fI/var/lib/logrotate/status\fR.

.TP
\fB-\-usage\fR
Prints a short usage message.

.SH CONFIGURATION FILE

\fBlogrotate\fR reads everything about the log files it should be handling
from the series of configuration files specified on the command line.  Each
configuration file can set global options (local definitions override
global ones, and later definitions override earlier ones) and specify
a logfile to rotate. A simple configuration file looks like this:

.nf
.ta +3i
# sample logrotate configuration file
compress

/var/log/messages {
    rotate 5
    weekly
    postrotate
        /sbin/killall -HUP syslogd
    endscript
}

"/var/log/httpd/access.log" /var/log/httpd/error.log {
    rotate 5
    mail www@xxxxxx
    size=100k
    sharedscripts
    postrotate
        /sbin/killall -HUP httpd
    endscript
}

/var/log/news/news.crit {
    monthly
    rotate 2
    olddir /var/log/news/old
    missingok
    postrotate
        kill -HUP `cat /var/run/inn.pid`
    endscript
    nocompress
}
.fi

.pp
The first few lines set global options; in the example, logs are
compressed after they are rotated.  Note that comments may appear
anywhere in the config file as long as the first non-whitespace
character on the line is a #.

The next section of the config files defined how to handle the log file
\fI/var/log/messages\fR. The log will go through five weekly rotations before
being removed. After the log file has been rotated (but before the old
version of the log has been compressed), the command 
\fI/sbin/killall -HUP syslogd\fR will be executed.

The next section defines the parameters for both
\fI/var/log/httpd/access.log\fR and \fI/var/log/httpd/error.log\fR.
They are rotated whenever is grows over 100k is size, and the old logs
files are mailed (uncompressed) to www@xxxxxx after going through 5
rotations, rather then being removed. The \fBsharedscripts\fR means that
the \fBpostrotate\fR script will only be run once, not once for each
log which is rotated. Note that the double quotes around the first filename
at the beginning of this section allows logrotate to rotate logs with
spaces in the name. Normal shell quoting rules apply, with ', ", and \\
characters supported.

The last section defines the parameters for all of the files in
\fI/var/log/news\fR. Each file is rotated on a monthly basis.  This is
considered a single rotation directive and if errors occur for more then
one file, the log files are not compressed.

Please use wildcards with caution.  If you specify *, \fBlogrotate\fR will
rotate all files, including previously rotated ones.  A way around this
is to use the \fBolddir\fR directive or a more exact wildcard (such as *.log).

Here is more information on the directives which may be included in
a \fBlogrotate\fR configuration file:

.TP
\fBcompress\fR
Old versions of log files are compressed with \fBgzip\fR by default. See also
\fBnocompress\fR. 

.TP
\fBcompresscmd\fR
Specifies which command to use to compress log files.  The default is
\fBgzip\fR.  See also \fBcompress\fR.

.TP
\fBuncompresscmd\fR
Specifies which command to use to uncompress log files.  The default is
\fBgunzip\fR.

.TP
\fBcompressext\fR
Specifies which extension to use on compressed logfiles, if compression
is enabled.  The default follows that of the configured compression
command.

.TP
\fBcompressoptions\fR
Command line options may be passed to the compression program, if one is
in use.  The default, for \fBgzip\fR, is "-9" (maximum compression).

.TP
\fBcopy\fR
Make a copy of the log file, but don't change the original at all.
This option can be used, for instance, to make a snapshot of the current
log file, or when some other utility needs to truncate or pare the file.
When this option is used, the \fBcreate\fR option will have no effect,
as the old log file stays in place.

.TP
\fBcopytruncate\fR
Truncate the original log file in place after creating a copy,
instead of moving the old log file and optionally creating a new one,
It can be used when some program can not be told to close its logfile
and thus might continue writing (appending) to the previous log file forever.
Note that there is a very small time slice between copying the file and
truncating it, so some logging data might be lost.
When this option is used, the \fBcreate\fR option will have no effect,
as the old log file stays in place.

.TP
\fBcreate \fImode\fR \fIowner\fR \fIgroup\fR
Immediately after rotation (before the \fBpostrotate\fR script is run)
the log file is created (with the same name as the log file just rotated).
\fImode\fR specifies the mode for the log file in octal (the same
as \fBchmod(2)\fR), \fIowner\fR specifies the user name who will own the
log file, and \fIgroup\fR specifies the group the log file will belong
to. Any of the log file attributes may be omitted, in which case those
attributes for the new file will use the same values as the original log
file for the omitted attributes. This option can be disabled using the
\fBnocreate\fR option.

.TP
\fBdaily\fR
Log files are rotated every day.

.TP
\fBdelaycompress\fR
Postpone compression of the previous log file to the next rotation cycle.
This has only effect when used in combination with \fBcompress\fR.
It can be used when some program can not be told to close its logfile
and thus might continue writing to the previous log file for some time.

.TP
\fBextension \fIext\fR
Log files are given the final extension \fIext\fR after rotation. If 
compression is used, the compression extension (normally \fB.gz\fR)
appears after \fIext\fR.

.TP
\fBifempty\fR
Rotate the log file even if it is empty, overiding the \fBnotifempty\fR
option (ifempty is the default).

.TP
\fBinclude \fIfile_or_directory\fR
Reads the file given as an argument as if it was included inline
where the \fBinclude\fR directive appears. If a directory is given,
most of the files in that directory are read in alphabetic order
before processing of the including file continues. The only files
which are ignored are files which are not regular files (such as
directories and named pipes) and files whose names end with one of
the taboo extensions, as specified by the \fBtabooext\fR directive.
The \fBinclude\fR directive may not appear inside of a log file
definition.

.TP
\fBmail \fIaddress\fR
When a log is rotated out-of-existence, it is mailed to \fIaddress\fR. If
no mail should be generated by a particular log, the \fBnomail\fR directive
may be used.

.TP
\fBmailfirst\fR
When using the \fBmail\fR command, mail the just-rotated file,
instead of the about-to-expire file.

.TP
\fBmaillast\fR
When using the \fBmail\fR command, mail the about-to-expire file,
instead of the just-rotated file (this is the default).

.TP
\fBmissingok\fR
If the log file is missing, go on to the next one without issuing an error
message. See also \fBnomissingok\fR.

.TP
\fBmonthly\fR
Log files are rotated the first time \fBlogrotate\fR is run in a month 
(this is normally on the first day of the month).

.TP
\fBnocompress\fR
Old versions of log files are not compressed with \fBgzip\fR. See also
\fBcompress\fR. 

.TP
\fBnocopy\fR
Do not copy the original log file and leave it in place.
(this overrides the \fBcopy\fR option).

.TP
\fBnocopytruncate\fR
Do not truncate the original log file in place after creating a copy
(this overrides the \fBcopytruncate\fR option).

.TP
\fBnocreate\fR
New log files are not created (this overrides the \fBcreate\fR option).

.TP
\fBnodelaycompress\fR
Do not postpone compression of the previous log file to the next rotation cycle
(this overrides the \fBdelaycompress\fR option).

.TP
\fBnomail\fR
Don't mail old log files to any address.

.TP
\fBnomissingok\fR
If a log file does not exist, issue an error. This is the default.

.TP
\fBnoolddir\fR
Logs are rotated in the same directory the log normally resides in (this 
overrides the \fBolddir\fR option).

.TP
\fBnosharedscripts\fR
Run \fBprerotate\fR and \fBpostrotate\fR scripts for every script which
is rotated (this is the default, and overrides the \fBsharedscripts\fR
option).

.TP
\fBnotifempty\fR
Do not rotate the log if it is empty (this overrides the \fBifempty\fR option).

.TP
\fBolddir \fIdirectory\fR
Logs are moved into \fIdirectory\fR for rotation. The \fIdirectory\fR must
be on the same physical device as the log file being rotated. When this
option is used all old versions of the log end up in \fIdirectory\fR.  This
option may be overriden by the \fBnoolddir\fR option.

.TP
\fBpostrotate\fR/\fBendscript\fR
The lines between \fBpostrotate\fR and \fBendscript\fR (both of which
must appear on lines by themselves) are executed after the log file is
rotated. These directives may only appear inside of a log file definition.
See \fBprerotate\fR as well.

.TP
\fBprerotate\fR/\fBendscript\fR
The lines between \fBprerotate\fR and \fBendscript\fR (both of which
must appear on lines by themselves) are executed before the log file is
rotated and only if the log will actually be rotated. These directives
may only appear inside of a log file definition.  See \fBpostrotate\fR
as well.

.TP
\fBfirstaction\fR/\fBendscript\fR
The lines between \fBfirstaction\fR and \fBendscript\fR (both of which
must appear on lines by themselves) are executed once before all log
files that match the wildcarded pattern are rotated, before prerotate script
is run and only if at least one log will actually be rotated. These directives
may only appear inside of a log file definition. See \fBlastaction\fR as well.

.TP
\fBlastaction\fR/\fBendscript\fR
The lines between \fBlastaction\fR and \fBendscript\fR (both of which
must appear on lines by themselves) are executed once after all log
files that match the wildcarded pattern are rotated, after postrotate script
is run and only if at least one log is rotated. These directives may only
appear inside of a log file definition. See \fBlastaction\fR as well.

.TP
\fBrotate \fIcount\fR
Log files are rotated <count> times before being removed or mailed to the
address specified in a \fBmail\fR directive. If \fIcount\fR is 0, old versions
are removed rather then rotated.

.TP
\fBsize \fIsize\fR
Log files are rotated when they grow bigger then \fIsize\fR bytes. If
\fIsize\fR is followed by \fIM\fR, the size if assumed to be in megabytes.
If the \fIk\fR is used, the size is in kilobytes. So \fBsize 100\fR,
\fIsize 100k\fR, and \fIsize 100M\fR are all valid.

.TP
\fBsharedscripts\fR
Normally, \fBprescript\fR and \fBpostscript\fR scripts are run for each
log which is rotated, meaning that a single script may be run multiple
times for log file entries which match multiple files (such as the 
/var/log/news/* example). If \fBsharedscript\fR is specified, the scripts
are only run once, no matter how many logs match the wildcarded pattern.
However, if none of the logs in the pattern require rotating, the scripts
will not be run at all. This option overrides the \fbnosharedscripts\fR
option.

.TP
\fBstart \fIcount\fR
This is the number to use as the base for rotation. For example, if
you specify 0, the logs will be created with a .0 extension as they are
rotated from the original log files.  If you specify 9, log files will
be created with a .9, skipping 0-8.  Files will still be rotated the
number of times specified with the \fBcount\fR directive.

.TP
\fBtabooext\fR [+] \fIlist\fR
The current taboo extension list is changed (see the \fBinclude\fR directive
for information on the taboo extensions). If a + precedes the list of
extensions, the current taboo extension list is augmented, otherwise it
is replaced. At startup, the taboo extension list 
contains .rpmorig, .rpmsave, ,v, .swp, .rpmnew, and ~.

.TP
\fBweekly\fR
Log files are rotated if the current weekday is less then the weekday
of the last rotation or if more then a week has passed since the last
rotation. This is normally the same as rotating logs on the first day
of the week, but it works better if \fIlogrotate\fR is not run every
night.

.SH FILES
.PD 0
.TP 27
\fI/var/lib/logrotate/status\fR
Default state file.
.TP 27
\fI/etc/logrotate.conf\fR
Configuration options.

.SH SEE ALSO
.IR gzip (1)

.SH AUTHORS
.nf
Erik Troan <ewt@xxxxxxxxxx>
.nf
Preston Brown <pbrown@xxxxxxxxxx>
.fi

--- NEW FILE: logrotate.c ---
#include <alloca.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <popt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

#include "basenames.h"
#include "log.h"
#include "logrotate.h"

typedef struct {
    char * fn;
[...965 lines suppressed...]
    }

    nowSecs = time(NULL);

    if (readState(stateFile, &states, &numStates)) {
        exit(1);
    }

    message(MESS_DEBUG, "\nHandling %d logs\n", numLogs);

    for (i = 0; i < numLogs; i++) {
        rc |= rotateLogSet(logs + i, &states, &numStates, force);
    }

    if (!debug && writeState(stateFile, states, numStates)) {
        exit(1);
    }

    return (rc != 0);
}

--- NEW FILE: logrotate.h ---
#ifndef H_LOGROTATE
#define H_LOGROTATE

#include <sys/types.h>
#include <glob.h>

#include "config.h"

#define LOG_FLAG_COMPRESS       (1 << 0)
#define LOG_FLAG_CREATE         (1 << 1)
#define LOG_FLAG_IFEMPTY        (1 << 2)
#define LOG_FLAG_DELAYCOMPRESS  (1 << 3)
#define LOG_FLAG_COPYTRUNCATE   (1 << 4)
#define LOG_FLAG_MISSINGOK      (1 << 5)
#define LOG_FLAG_MAILFIRST      (1 << 6)
#define LOG_FLAG_SHAREDSCRIPTS  (1 << 7)
#define LOG_FLAG_COPY           (1 << 8)

#define NO_FORCE_ROTATE 0
#define FORCE_ROTATE    1

typedef struct {
    char * pattern;
    char ** files;
    int numFiles;
    char * oldDir;
    enum { ROT_DAYS, ROT_WEEKLY, ROT_MONTHLY, ROT_SIZE, ROT_FORCE } criterium;
    unsigned int threshhold;
    int rotateCount;
    int logStart;
    char * pre, * post, * first, * last;
    char * logAddress;
    char * extension;
    char * compress_prog;
    char * uncompress_prog;
    char * compress_options;
    char * compress_ext;
    int flags;
    mode_t createMode;          /* if any/all of these are -1, we use the */
    uid_t createUid;            /* attributes from the log file just rotated */
    gid_t createGid;
} logInfo;

int readConfigPath(const char * path, logInfo * defConfig, 
                          logInfo ** logsPtr, int * numLogsPtr);

extern int debug;

#endif

--- NEW FILE: logrotate.spec ---
Summary: Rotates, compresses, removes and mails system log files.
Name: logrotate
Version: 3.6.8
Release: 1
License: GPL
Group: System Environment/Base
Source: logrotate-%{PACKAGE_VERSION}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}.root

%description
The logrotate utility is designed to simplify the administration of
log files on a system which generates a lot of log files.  Logrotate
allows for the automatic rotation compression, removal and mailing of
log files.  Logrotate can be set to handle a log file daily, weekly,
monthly or when the log file gets to a certain size.  Normally,
logrotate runs as a daily cron job.

Install the logrotate package if you need a utility to deal with the
log files on your system.

%prep
%setup

%build
make RPM_OPT_FLAGS="$RPM_OPT_FLAGS"

%install
rm -rf $RPM_BUILD_ROOT
make PREFIX=$RPM_BUILD_ROOT MANDIR=%{_mandir} install
mkdir -p $RPM_BUILD_ROOT/etc/logrotate.d
mkdir -p $RPM_BUILD_ROOT/etc/cron.daily
mkdir -p $RPM_BUILD_ROOT/var/lib

install -m 644 examples/logrotate-default $RPM_BUILD_ROOT/etc/logrotate.conf
install -m 755 examples/logrotate.cron $RPM_BUILD_ROOT/etc/cron.daily/logrotate
touch $RPM_BUILD_ROOT/var/lib/logrotate.status

%clean
rm -rf $RPM_BUILD_ROOT

%files
%defattr(-,root,root)
%doc CHANGES
%attr(0755, root, root) /usr/sbin/logrotate
%attr(0644, root, root) %{_mandir}/man8/logrotate.8*
%attr(0755, root, root) /etc/cron.daily/logrotate
%attr(0644, root, root) %config(noreplace) /etc/logrotate.conf
%attr(0755, root, root) %dir /etc/logrotate.d
%attr(0644, root, root) %verify(not size md5 mtime) %config(noreplace) 
/var/lib/logrotate.status

%changelog
* Mon Jan 20 2003 Elliot Lee <sopwith@xxxxxxxxxx> 3.6.8-1
- Old patch from pm@xxxxxxxxxx

* Tue Jan 14 2003 Elliot Lee <sopwith@xxxxxxxxxx> 3.6.7-1
- Fixes from bugzilla

* Fri Nov 15 2002 Elliot Lee <sopwith@xxxxxxxxxx> 3.6.6-1
- Commit patch from Fidelis Assis <fidelis@xxxxxxxxxxxxxxx>

* Thu Jun 20 2002 Elliot Lee <sopwith@xxxxxxxxxx> 3.6.5-1
- Commit fix for #65299

* Mon Apr 15 2002 Elliot Lee <sopwith@xxxxxxxxxx> 3.6.4-1
- Commit fix for #62560

* Wed Mar 13 2002 Elliot Lee <sopwith@xxxxxxxxxx> 3.6.3-1
- Apply various bugfix patches from the openwall people

* Tue Jan 29 2002 Elliot Lee <sopwith@xxxxxxxxxx> 3.6.2-1
- Fix bug #55809 (include logrotate.status in %files)
- Fix bug #58328 (incorrect error detection when reading state file)
- Allow 'G' size specifier from bug #57242

* Mon Dec 10 2001 Preston Brown <pbrown@xxxxxxxxxx>
- noreplace config file

* Wed Nov 28 2001 Preston Brown <pbrown@xxxxxxxxxx> 3.6-1
- patch from Alexander Kourakos <awk@xxxxxxxx> to stop the shared
  postrotate/prerotate scripts from running if none of the log(s) need
  rotating.  All log files are now checked for rotation in one batch,
  rather than sequentially.
- more fixes from Paul Martin <pm@xxxxxxxxxx>

* Thu Nov  8 2001 Preston Brown <pbrown@xxxxxxxxxx> 3.5.10-1
- fix from paul martin <pm@xxxxxxxxxx> for zero-length state files

* Tue Sep  4 2001 Preston Brown <pbrown@xxxxxxxxxx>
- fix segfault when logfile is in current directory.

* Tue Aug 21 2001 Preston Brown <pbrown@xxxxxxxxxx>
- fix URL for source location

* Thu Aug  2 2001 Preston Brown <pbrown@xxxxxxxxxx>
- man page cleanups, check for negative rotation counts

* Mon Jul  2 2001 Preston Brown <pbrown@xxxxxxxxxx>
- more minor manpage updates (#45625)

* Thu Jun 21 2001 Preston Brown <pbrown@xxxxxxxxxx> 3.5.6-1
- enable LFS support (debian bug #100810)
- quote filenames for running compress commands or pre/postrotate cmds (#21348)
- deprecate "errors" directive (see bug #16544 for explanation)
- update man page
- configurable compression command by Colm Buckley <colm@xxxxxxxxxx>

* Fri Jun  1 2001 Preston Brown <pbrown@xxxxxxxxxx> 3.5.5-1
- be less strict about whitespace near filenames.  Patch from Paul Martin 
<pm@xxxxxxxxxx>.

* Thu Jan  4 2001 Bill Nottingham <notting@xxxxxxxxxx>
- %%defattr

* Wed Jan 03 2001 Preston Brown <pbrown@xxxxxxxxxx>
- see CHANGES

* Tue Aug 15 2000 Erik Troan <ewt@xxxxxxxxxx>
- see CHANGES

* Sun Jul 23 2000 Erik Troan <ewt@xxxxxxxxxx>
- see CHANGES

* Tue Jul 11 2000 Erik Troan <ewt@xxxxxxxxxx>
- support spaces in filenames
- added sharedscripts

* Sun Jun 18 2000 Matt Wilson <msw@xxxxxxxxxx>
- use %%{_mandir} for man pages

* Thu Feb 24 2000 Erik Troan <ewt@xxxxxxxxxx>
- don't rotate lastlog

* Thu Feb 03 2000 Erik Troan <ewt@xxxxxxxxxx>
- gzipped manpages



-------------------------------------------------------
SF.Net is sponsored by: Speed Start Your Linux Apps Now.
Build and deploy apps & Web services for Linux with
a free DVD software kit from IBM. Click Now!
http://ads.osdn.com/?ad_id=1356&alloc_id=3438&op=click


<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

Recently Viewed:
version-control...    qnx.openqnx.dev...    redhat.rhn.user...    ietf.openpgp/20...    mail.mutt.user/...    web.microformat...    java.sync4j.use...    education.ezpro...    user-groups.blu...    solaris.manager...    org.fitug.debat...    technology.erps...    politics.activi...    linux.redhat.fe...    bug-tracking.ma...    xfce.user/2004-...    hams/2004-11/ms...    kde.users.pim/2...    culture.cooking...    freebsd.devel.x...    gnu.m4.adhoc/20...    ngpt.user/2002-...    apple.fink.deve...   
Home | advertise | OSDir is an inevitable website. super tiny logo

Free Magazines

Cisco News
Receive a free quarterly e-newsletter with exclusive articles on how Cisco IT uses its own products and solutions to enable the business.
subscribe

Systems Management News, the newspaper for IT systems administration and data center managers! Each issue of Systems Management News is chock-full of news and analysis to help you understand what's happening in your field.
subscribe

The Enterprise Newsweekly eWeek is the essential technology information source for builders of e-business.
subscribe

Oracle Magazine Oracle Magazine contains technology strategy articles, sample code, tips, Oracle and partner news, how to articles for developers and DBAs, and more. Oracle (NASDAQ: ORCL) is the world's largest enterprise software company.
subscribe

Total Telecom Total Telecom is "The Economist of the communications industry".
subscribe

Navigation