Compare commits

...

No commits in common. "pristine-tar" and "master" have entirely different histories.

110 changed files with 33148 additions and 3 deletions

8
AUTHORS Normal file
View File

@ -0,0 +1,8 @@
Original vixie-cron was written by Paul Vixie.
Significant contributors:
Marcela Mašláňová <mmaslano@redhat.com>
Colin Dean <colin@colin-dean.org>
Tomáš Mráz <tmraz@fedoraproject.org>
Marco Migliori <sgerwk@aol.com>
Sami Kerola <kerolasa@iki.fi>

417
COPYING Normal file
View File

@ -0,0 +1,417 @@
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
*
* 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.
*/
/*
* Copyright (c) 1988, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software written by Ken Arnold and
* published in UNIX Review, Vol. 6, No. 8.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Paul Vixie.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)bitstring.h 8.1 (Berkeley) 7/19/93
*/
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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) <year> <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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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 Lesser General
Public License instead of this License.

339
COPYING.anacron Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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) <year> <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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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 Lesser General
Public License instead of this License.

510
COPYING.obstack Normal file
View File

@ -0,0 +1,510 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations
below.
When we speak of free software, we are referring to freedom of use,
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 and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it
becomes a de-facto standard. To achieve this, non-free programs must
be allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, 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 library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete 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 distribute a copy of this License along with the
Library.
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 Library or any portion
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
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 Library, 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 Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you 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.
If distribution of 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 satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at least
three years, to give the same user the materials specified in
Subsection 6a, above, for a charge no more than the cost of
performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be 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.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library 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.
9. 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 Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
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 with
this License.
11. 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 Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library 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 Library.
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.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library 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.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser 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 Library
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 Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
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
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "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
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. 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 LIBRARY 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
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms
of the ordinary General Public License).
To apply these terms, attach the following notices to the library.
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 library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or
your school, if any, to sign a "copyright disclaimer" for the library,
if necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James
Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

2730
ChangeLog Normal file

File diff suppressed because it is too large Load Diff

17
INSTALL Normal file
View File

@ -0,0 +1,17 @@
Basic Installation
==================
Run the usual autotools combination of:
./configure
make
make install
If no "configure" script is present, generate it by:
./autogen.sh
The executable files will be installed in /usr/local/* by default.
Configure Options
=================
Please see the ./configure --help output for available build-time
options.

36
Makefile.am Normal file
View File

@ -0,0 +1,36 @@
AM_CFLAGS = -I$(top_srcdir)
BUILT_SOURCES =
CLEANFILES =
EXTRA_DIST =
bin_PROGRAMS =
common_nodist =
sbin_PROGRAMS =
dist_noinst_HEADERS = \
cronie_common.h
EXTRA_DIST += \
README.anacron \
COPYING.anacron \
COPYING.obstack \
obstack/obstack.h \
cronie.init \
crond.sysconfig \
contrib/anacrontab \
contrib/0anacron \
contrib/0hourly \
contrib/dailyjobs \
contrib/cronie.systemd \
anacron/ChangeLog.anacron
if PAM
pamdir = $(sysconfdir)/pam.d
dist_pam_DATA = pam/crond
else
EXTRA_DIST += pam/crond
endif
include anacron/Makemodule.am
include man/Makemodule.am
include src/Makemodule.am

1388
Makefile.in Normal file

File diff suppressed because it is too large Load Diff

79
NEWS Normal file
View File

@ -0,0 +1,79 @@
cronie NEWS -- history of user-visible changes.
Release 1.6.1
* crond: Fix regression of handling ranges (x-y) in crontab
Release 1.6.0
* crond: Add switch -f as an alias for -n
* crond: Add random within range '~' operator
* crond: Use the configure runstatedir directory for pid file
* crond: Increase the maximum number of crontab entries to 10000
Release 1.5.7
* anacron: Fix problem of anacron not being started on some desktops
* crontab: switch off colors if NO_COLOR is set
Release 1.5.6
* crontab: crontab without arguments now works if stdin is not a TTY
* crond: Fix various issues on loading the crontab databases on startup
* anacron: Expand MAILTO and MAILFROM environment variables
* crontab: New option to test crontab file syntax without installing it
Release 1.5.5
* Explicitly validate upper end of range and step to disallow entries
such as: 1-234/5678 * * * * ....
* crond: Report missing newline before EOF in syslog so the line is not
completely silently ignored.
* crontab -l colors comment lines in a different color.
* crond: Revert "Avoid creating pid files when crond doesn't fork".
* anacron is built by default.
* Use non-recursive build.
* cronnext: Allow to optionally select jobs by substring.
Release 1.5.4
* crond: Fix regression from previous release. Only first job from a crontab
was being run.
Release 1.5.3
* Fix CVE-2019-9704 and CVE-2019-9705 to avoid local DoS of the crond.
* crontab: Make crontab without arguments fail.
* crond: In PAM configuration include system-auth instead of password-auth.
* crond: In the systemd service file restart crond if it fails.
* crond: Use the role from the crond context for system job contexts.
* Multiple small cleanups and fixes.
Release 1.5.2
* cronnext: New useful utility to find out time of the next job run.
* crond: Avoid creating PID files when crond doesn't fork.
* crontab: Do not try to replace the crontab with a directory.
* crond: Log startup even when started in non-forking mode.
* Multiple small cleanups and fixes.
Release 1.5.1
* crontab: Use temporary file name that is ignored by crond.
* crond: Inherit PATH from the crond environment if -P option is used.
* crond: Remove hardcoded "system_u" SELinux user, use the SELinux user
of the running crond.
* anacron: Small cleanups and fixes.
* crond: Fix longstanding race condition on repeated crontab modification.
Release 1.5.0
* First release with NEWS. :)
* crond: Job environment variables are set also when executing sendmail.
* crond: Adding duplicate orphans on reload is now prevented.
* crond: The regular crond shutdown is now logged.
* crontab: PAM is not called in crontab command if the caller's uid is 0.
* crond: PAM is not called from crond for system cron jobs
(/etc/crontab, /etc/cron.d) which are run for uid 0.
* crond: The existence of an user is checked at time when job is run
and not when the crontab is parsed on database reload.

13
README Normal file
View File

@ -0,0 +1,13 @@
Cronie contains the standard UNIX daemon crond that runs specified programs at
scheduled times and related tools. The source is based on the original vixie-cron
and has security and configuration enhancements like the ability to use pam and
SELinux.
And why cronie? See http://www.urbandictionary.com/define.php?term=cronie
Contact
-------
Mailing list: `cronie-devel AT lists.fedorahosted DOT org`
Bug reports and pull requests can be filled at the Github site.

142
README.anacron Normal file
View File

@ -0,0 +1,142 @@
What is Anacron ?
-----------------
Anacron is a periodic command scheduler. It executes commands at
intervals specified in days. Unlike cron, it does not assume that the
system is running continuously. It can therefore be used to control
the execution of daily, weekly and monthly jobs (or anything with a
period of n days), on systems that don't run 24 hours a day. When
installed and configured properly, Anacron will make sure that the
commands are run at the specified intervals as closely as
machine-uptime permits.
Every time Anacron is run, it reads a configuration file that
specifies the jobs Anacron controls, and their periods in days. If a
job wasn't executed in the last n days, where n is the period of that
job, Anacron executes it. Anacron then records the date in a special
timestamp file that it keeps for each job, so it can know when to run
it again. When all the executed commands terminate, Anacron exits.
It is recommended to run Anacron from the system boot-scripts.
This way the jobs "whose time has come" will be run shortly after the
machine boots. A delay can be specified for each job so that the
machine isn't overloaded at boot time.
In addition to running Anacron from the boot-scripts, it is also
recommended to schedule it as a daily cron-job (usually at an early
morning hour), so that if the machine is kept running for a night,
jobs for the next day will still be executed.
Why this may be useful ?
------------------------
Most Unix-like systems have daily, weekly and monthly scripts that
take care of various "housekeeping chores" such as log-rotation,
updating the "locate" and "man" databases, etc. Daily scripts are
usually scheduled as cron-jobs to execute around 1-7 AM. Weekly
scripts are scheduled to run on Sundays. On machines that are turned
off for the night or for the weekend, these scripts rarely get run.
Anacron solves this problem. These jobs can simply be scheduled as
Anacron-jobs with periods of 1, 7 and a special target called @monthly.
What Anacron is not ?
---------------------
Anacron is not an attempt to make cron redundant. It cannot
currently be used to schedule commands at intervals smaller than days.
It also does not guarantee that the commands will be executed at any
specific day or hour.
It isn't a full-time daemon. It has to be executed from boot
scripts, from cron-jobs, or explicitly.
For more details, see the anacron(8) manpage.
Requirements
------------
- A Linux system. (maybe other *NIX systems)
- A functioning syslog daemon.
- A functioning /usr/lib/sendmail command. (all MTAs should have
that).
Compilation and Installation
----------------------------
- Untar the source package.
- Check the Makefile. Edit as required.
- Check the top of "global.h". You may want to change the syslog
facility and priorities, and the path to your MTA's sendmail
compatible command (/usr/lib/sendmail).
- cd to the directory.
- Type "make".
You can safely ignore warnings of the form: "*.d: No such file or
directory"
- Become root. Type "make install".
Setup
-----
1. Locate your system's daily, weekly and monthly cron-jobs.
See your cron documentation for more details.
2. Decide which of these jobs should be controlled by Anacron.
Remember that Anacron does not guarantee execution at any specific
day of the month, day of the week, or time of day. Jobs for which
the timing is critical should probably not be controlled by
Anacron.
3. Comment these jobs out of their crontab files. (You may have to
use the "crontab" command for this. See the cron documentation.)
4. Put them in /etc/anacrontab. Note that the format is not the same
as the crontab entries. See the anacrontab(5) manpage. Here's an
example from a typical Debian system:
-----Cut
# /etc/anacrontab example
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
# format: period delay job-identifier command
1 5 cron.daily run-parts /etc/cron.daily
7 10 cron.weekly run-parts /etc/cron.weekly
@monthly 15 cron.monthly run-parts /etc/cron.monthly
-----Cut
5. Put the command "anacron -s" somewhere in your boot-scripts.
Make sure that syslogd is started before this command.
6. Schedule the command "anacron -s" as a daily cron-job (preferably
at some early morning hour). This will make sure that jobs are run
when the systems is left running for a night.
That's it.
It is a good idea to check what your daily, weekly and monthly scripts
actually do, and disable any parts that may be irrelevant for your
system.
Credits
-------
Anacron was originally conceived and implemented by Christian Schwarz
<schwarz@monet.m.isar.de>.
The current implementation is a complete rewrite by Itai Tzur
<itzur@actcom.co.il>.
Current code base maintained by Sean 'Shaleh' Perry <shaleh@(debian.org|valinux.com)>.

1137
aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

7
anacron-paths.h Normal file
View File

@ -0,0 +1,7 @@
/* This file has been automatically generated. Do not edit. */
#ifndef _ANACRON_PATHS_H_
#define _ANACRON_PATHS_H_
#define ANACRON_SPOOL_DIR "/usr/local/var/spool/anacron"
#define ANACRONTAB "/usr/local/etc/anacrontab"
#endif /* _ANACRON_PATHS_H_ */

39
anacron/ChangeLog.anacron Normal file
View File

@ -0,0 +1,39 @@
Changes in Anacron 2.3.1
------------------------
* documentation no longer suggests adding local directories to the PATH
Changes in Anacron 2.3
----------------------
* anacron can now read an arbitrary anacrontab file, use the -t option
Changes in Anacron 2.1/2.2
--------------------------
* Sean 'Shaleh' Perry <shaleh@(debian.org|valinux.com)> is now maintainer
* if timestamp is from the future, re-run job
* ansi cleanup / code cleaning
Changes in Anacron 2.0.1
------------------------
* Minor cosmetic changes to log messages.
* Jobs are now started with "/" as their working directory. This is
more compatible with older Anacron versions, avoids annoying errors on
some systems, and generally seems to make more sense.
Summary of major changes in Anacron 2.0
---------------------------------------
* Complete rewrite in C. Should be backwards compatible with existing
Anacron installations.
* First release as a "generic" Linux package (was a Debian package).
* No longer needs special lock-files. Locking is done on the timestamp
files.
* Sends log messages to syslogd. There's no log file now.
* Output of jobs, if any, is mailed to the user.
* Added command line options: -s -f -n -d -q -u -V -h. See the manpage.
* Specific jobs can now be selected on the command line.
* Added SIGUSR1 handling, to cleanly stop execution.
* Jobs will now be started with their current directory set to the home
of the user running Anacron (usually root).

42
anacron/Makemodule.am Normal file
View File

@ -0,0 +1,42 @@
# Makefile.am - two binaries crond and crontab
if ANACRON
sbin_PROGRAMS += anacron/anacron
anacron_anacron_SOURCES = \
anacron-paths.h \
anacron/global.h \
anacron/gregor.c \
anacron/gregor.h \
anacron/lock.c \
anacron/log.c \
anacron/main.c \
anacron/matchrx.c \
anacron/matchrx.h \
anacron/readtab.c \
anacron/runjob.c \
cronie_common.c
common_nodist += anacron-paths.h
nodist_anacron_anacron_SOURCES = $(common_nodist)
BUILT_SOURCES += $(common_nodist)
if NEED_OBSTACK
anacron_anacron_SOURCES += obstack/obstack.c
endif
anacron_anacron_LDADD = $(LIBSELINUX) $(LIBPAM) $(LIBAUDIT)
# This header contains all the paths.
# If they are configurable, they are declared in configure script.
# Depends on this Makefile, because it uses make variables.
CLEANFILES += anacron-paths.h
anacron-paths.h: Makefile
@echo 'creating $@'
@sed >$@ 's/ *\\$$//' <<\END #\
/* This file has been automatically generated. Do not edit. */ \
\
#ifndef _ANACRON_PATHS_H_ \
#define _ANACRON_PATHS_H_ \
#define ANACRON_SPOOL_DIR "$(ANACRON_SPOOL_DIR)" \
#define ANACRONTAB "$(ANACRONTAB)" \
#endif /* _ANACRON_PATHS_H_ */ \
END
endif

163
anacron/global.h Normal file
View File

@ -0,0 +1,163 @@
/*
Anacron - run commands periodically
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>
Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net>
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.
The GNU General Public License can also be found in the file
`COPYING' that comes with the Anacron source distribution.
*/
#ifndef _ANACRON_GLOBAL_H
#define _ANACRON_GLOBAL_H
/* Syslog facility and priorities messages will be logged to (see syslog(3)).
* If you change these, please update the man page. */
#define SYSLOG_FACILITY LOG_CRON
#define EXPLAIN_LEVEL LOG_NOTICE /* informational messages */
#define COMPLAIN_LEVEL LOG_ERR /* error messages */
#define DEBUG_LEVEL LOG_DEBUG /* only used when DEBUG is defined */
/* Mail interface. (All MTAs should supply this command) */
#define SENDMAIL "/usr/sbin/sendmail"
/* End of user-configurable section */
#define FAILURE_EXIT 1
#define MAX_MSG 150
#include <signal.h>
#include <time.h>
#include "anacron-paths.h"
#include "cronie_common.h"
/* Some declarations */
struct env_rec1 {
char *assign;
struct env_rec1 *next;
};
typedef struct env_rec1 env_rec;
struct job_rec1 {
int period;
int named_period;
int delay;
char *ident;
char *command;
char *mailto;
int tab_line;
int arg_num;
int timestamp_fd;
int input_fd;
int output_fd;
off_t mail_header_size;
pid_t job_pid;
pid_t mailer_pid;
int drop_job;
struct job_rec1 *next;
env_rec *prev_env_rec;
};
typedef struct job_rec1 job_rec;
/* Global variables */
extern pid_t primary_pid;
extern char *program_name;
extern char *anacrontab;
extern char *spooldir;
extern int old_umask;
extern sigset_t old_sigmask;
extern int serialize,force,update_only,now,no_daemon,quiet,testing_only;
extern int day_now;
extern int year,month,day_of_month;
extern int in_background;
extern job_rec *first_job_rec;
extern env_rec *first_env_rec;
extern char **job_args;
extern int job_nargs;
extern int njobs;
extern job_rec **job_array;
extern int running_jobs,running_mailers;
extern int complaints;
extern time_t start_sec;
/* time ranges for START_HOURS_RANGE */
extern int range_start;
extern int range_stop;
/* preferred hour for jobs */
extern int preferred_hour;
/* Function prototypes */
/* main.c */
int xopen(int fd, const char *file_name, int flags);
void xclose(int fd);
pid_t xfork(void);
#ifdef __GNUC__
#define PRINTF_FORMAT(n, m) \
__attribute__ ((format (printf, n, m)))
#else
#define PRINTF_FORMAT(n, m)
#endif
/* log.c */
void explain(const char *fmt, ...)PRINTF_FORMAT(1,2);
void explain_e(const char *fmt, ...)PRINTF_FORMAT(1,2);
void complain(const char *fmt, ...)PRINTF_FORMAT(1,2);
void complain_e(const char *fmt, ...)PRINTF_FORMAT(1,2);
void die(const char *fmt, ...)PRINTF_FORMAT(1,2) ATTRIBUTE_NORETURN;
void die_e(const char *fmt, ...)PRINTF_FORMAT(1,2) ATTRIBUTE_NORETURN;
void xdebug(const char *fmt, ...)PRINTF_FORMAT(1,2);
void xdebug_e(const char *fmt, ...)PRINTF_FORMAT(1,2);
void xcloselog(void);
#ifdef DEBUG
#define Debug(args) xdebug args
#define Debug_e(args) xdebug_e args
#else /* not DEBUG */
#define Debug(args) (void)(0)
#define Debug_e(args) (void)(0)
#endif /* not DEBUG */
/* readtab.c */
void read_tab(int cwd);
void arrange_jobs(void);
/* lock.c */
int consider_job(job_rec *jr);
void unlock(job_rec *jr);
void update_timestamp(job_rec *jr);
void fake_job(job_rec *jr);
/* runjob.c */
void tend_children();
void launch_job(job_rec *jr);
#endif

181
anacron/gregor.c Normal file
View File

@ -0,0 +1,181 @@
/*
Anacron - run commands periodically
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>
Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net>
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.
The GNU General Public License can also be found in the file
`COPYING' that comes with the Anacron source distribution.
*/
#include <limits.h>
#include <time.h>
#include "gregor.h"
static const int
days_in_month[] = {
31, /* Jan */
28, /* Feb (non-leap) */
31, /* Mar */
30, /* Apr */
31, /* May */
30, /* Jun */
31, /* Jul */
31, /* Aug */
30, /* Sep */
31, /* Oct */
30, /* Nov */
31 /* Dec */
};
static int leap(int year);
int
day_num(int year, int month, int day)
/* Return the "day number" of the date year-month-day according to the
* "proleptic Gregorian calendar".
* If the given date is invalid, return -1.
*
* Here, "day number" is defined as the number of days since December 31,
* 1 B.C. (Gregorian). (January 1, 1 A.D. is day number 1 etc...)
*
* The Gregorian calendar was instituted by Pope Gregory XIII in 1582,
* and has gradually spread to become the international standard calendar.
* The proleptic Gregorian calendar is formed by projecting the date system
* of the Gregorian calendar to dates before its adoption.
*
* For more details, see:
* http://astro.nmsu.edu/~lhuber/leaphist.html
* http://www.magnet.ch/serendipity/hermetic/cal_stud/cal_art.htm
* and your local library.
*/
{
int dn;
int i;
int isleap; /* save three calls to leap() */
/* Some validity checks */
/* we don't deal with B.C. years here */
if (year < 1) return - 1;
/* conservative overflow estimate */
if (year > (INT_MAX / 366)) return - 1;
if (month > 12 || month < 1) return - 1;
if (day < 1) return - 1;
isleap = leap(year);
if (month != 2) {
if(day > days_in_month[month - 1]) return - 1;
}
else if ((isleap && day > 29) || (!isleap && day > 28))
return - 1;
/* First calculate the day number of December 31 last year */
/* save us from doing (year - 1) over and over */
i = year - 1;
/* 365 days in a "regular" year + number of leap days */
dn = (i * 365) + ((i / 4) - (i / 100) + (i / 400));
/* Now, day number of the last day of the previous month */
for (i = month - 1; i > 0; --i)
dn += days_in_month[i - 1];
/* Add 29 February ? */
if (month > 2 && isleap) ++dn;
/* How many days into month are we */
dn += day;
return dn;
}
static int
leap(int year)
/* Is this a leap year ? */
{
/* every year exactly divisible by 4 is "leap" */
/* unless it is exactly divisible by 100 */
/* but not by 400 */
return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
}
int
days_last_month (void)
/* How many days did last month have? */
{
struct tm time_record;
time_t current_time;
time (&current_time);
localtime_r (&current_time, &time_record);
switch (time_record.tm_mon) {
case 0: return days_in_month[11];
case 2: return days_in_month[1] + (leap (time_record.tm_year + 1900) ? 1 : 0);
default: return days_in_month[time_record.tm_mon - 1];
}
}
int
days_this_month (void)
/* How many days does this month have? */
{
struct tm time_record;
time_t current_time;
time (&current_time);
localtime_r (&current_time, &time_record);
switch (time_record.tm_mon) {
case 1: return days_in_month[1] + (leap (time_record.tm_year + 1900) ? 1 : 0);
default: return days_in_month[time_record.tm_mon];
}
}
int
days_last_year (void)
/* How many days this last year have? */
{
struct tm time_record;
time_t current_time;
time (&current_time);
localtime_r (&current_time, &time_record);
if (leap(time_record.tm_year - 1 + 1900)) {
return 366;
}
return 365;
}
int
days_this_year (void)
/* How many days does this year have */
{
struct tm time_record;
time_t current_time;
time (&current_time);
localtime_r (&current_time, &time_record);
if (leap(time_record.tm_year + 1900)) {
return 366;
}
return 365;
}

30
anacron/gregor.h Normal file
View File

@ -0,0 +1,30 @@
/*
Anacron - run commands periodically
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>
Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net>
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.
The GNU General Public License can also be found in the file
`COPYING' that comes with the Anacron source distribution.
*/
int day_num(int year, int month, int day);
int days_last_month (void);
int days_this_month (void);
int days_last_year (void);
int days_this_year (void);

217
anacron/lock.c Normal file
View File

@ -0,0 +1,217 @@
/*
Anacron - run commands periodically
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>
Copyirght (C) 2004 Pascal Hakim <pasc@redellipse.net>
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.
The GNU General Public License can also be found in the file
`COPYING' that comes with the Anacron source distribution.
*/
/* Lock and timestamp management
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include "global.h"
#include "gregor.h"
static void
open_tsfile(job_rec *jr)
/* Open the timestamp file for job jr */
{
jr->timestamp_fd = open(jr->ident, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (jr->timestamp_fd == -1)
die_e("Can't open timestamp file for job %s", jr->ident);
fcntl(jr->timestamp_fd, F_SETFD, 1); /* set close-on-exec flag */
/* We want to own this file, and set its mode to 0600. This is necessary
* in order to prevent other users from putting locks on it. */
if (fchown(jr->timestamp_fd, getuid(), getgid()))
die_e("Can't chown timestamp file %s", jr->ident);
if (fchmod(jr->timestamp_fd, S_IRUSR | S_IWUSR))
die_e("Can't chmod timestamp file %s", jr->ident);
}
static int
lock_file(int fd)
/* Attempt to put an exclusive fcntl() lock on file "fd"
* Return 1 on success, 0 on failure.
*/
{
int r;
struct flock sfl;
sfl.l_type = F_WRLCK;
sfl.l_start = 0;
sfl.l_whence = SEEK_SET;
sfl.l_len = 0; /* we lock all the file */
errno = 0;
r = fcntl(fd, F_SETLK, &sfl);
if (r != -1) return 1;
if (errno != EACCES && errno != EAGAIN)
die_e("fcntl() error");
return 0;
}
int
consider_job(job_rec *jr)
/* Check the timestamp of the job. If "its time has come", lock the job
* and return 1, if it's too early, or we can't get the lock, return 0.
*/
{
char timestamp[9];
int ts_year, ts_month, ts_day, dn;
ssize_t b;
open_tsfile(jr);
/* read timestamp */
b = read(jr->timestamp_fd, timestamp, 8);
if (b == -1) die_e("Error reading timestamp file %s", jr->ident);
timestamp[8] = 0;
/* is it too early? */
if (!force && b == 8)
{
int day_delta;
time_t jobtime;
struct tm *t;
if (sscanf(timestamp, "%4d%2d%2d", &ts_year, &ts_month, &ts_day) == 3)
dn = day_num(ts_year, ts_month, ts_day);
else
dn = 0;
day_delta = day_now - dn;
/*
* if day_delta is negative, we assume there was a clock skew
* and re-run any affected jobs
* otherwise we check if the job's time has come
*/
if (day_delta >= 0 && day_delta < jr->period)
{
/* yes, skip job */
xclose(jr->timestamp_fd);
return 0;
}
/*
* Check to see if it's a named period, in which case we need
* to figure it out.
*/
if (jr->named_period)
{
int period = 0, bypass = 0;
switch (jr->named_period)
{
case 1: /* monthly */
period = days_last_month ();
bypass = days_this_month ();
break;
case 2: /* yearly, annually */
period = days_last_year ();
bypass = days_this_year ();
break;
case 3: /* daily */
period = 1;
bypass = 1;
break;
case 4: /* weekly */
period = 7;
bypass = 7;
break;
default:
die ("Unknown named period for %s (%d)", jr->ident, jr->named_period);
}
printf ("Checking against %d with %d\n", day_delta, period);
if (day_delta < period && day_delta != bypass)
{
/* Job is still too young */
xclose (jr->timestamp_fd);
return 0;
}
}
jobtime = start_sec + jr->delay * 60;
t = localtime(&jobtime);
if (!now && preferred_hour != -1 && t->tm_hour != preferred_hour) {
Debug(("The job's %s preferred hour %d was missed, skipping the job.", jr->ident, preferred_hour));
xclose (jr->timestamp_fd);
return 0;
}
if (!now && range_start != -1 && range_stop != -1 &&
(t->tm_hour < range_start || t->tm_hour >= range_stop))
{
Debug(("The job `%s' falls out of the %02d:00-%02d:00 hours range, skipping.",
jr->ident, range_start, range_stop));
xclose (jr->timestamp_fd);
return 0;
}
}
/* no! try to grab the lock */
if (lock_file(jr->timestamp_fd)) return 1; /* success */
/* didn't get lock */
xclose(jr->timestamp_fd);
explain("Job `%s' locked by another anacron - skipping", jr->ident);
return 0;
}
void
unlock(job_rec *jr)
{
xclose(jr->timestamp_fd);
}
void
update_timestamp(job_rec *jr)
/* We write the date "now". "Now" can be either the time when anacron
* started, or the time when the job finished.
* I'm not quite sure which is more "right", but I've decided on the first
* option.
* Note that this is not the way it was with anacron 1.0.3 to 1.0.7.
*/
{
char stamp[10];
snprintf(stamp, 10, "%04d%02d%02d\n", year, month, day_of_month);
if (lseek(jr->timestamp_fd, 0, SEEK_SET))
die_e("Can't lseek timestamp file for job %s", jr->ident);
if (write(jr->timestamp_fd, stamp, 9) != 9)
die_e("Can't write timestamp file for job %s", jr->ident);
if (ftruncate(jr->timestamp_fd, 9))
die_e("ftruncate error");
}
void
fake_job(job_rec *jr)
/* We don't bother with any locking here. There's no point. */
{
open_tsfile(jr);
update_timestamp(jr);
xclose(jr->timestamp_fd);
}

230
anacron/log.c Normal file
View File

@ -0,0 +1,230 @@
/*
Anacron - run commands periodically
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>
Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net>
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.
The GNU General Public License can also be found in the file
`COPYING' that comes with the Anacron source distribution.
*/
/* Error logging
*
* We have two levels of logging (plus debugging if DEBUG is defined):
* "explain" level for informational messages, and "complain" level for errors.
*
* We log everything to syslog, see the top of global.h for relevant
* definitions.
*
* Stderr gets "complain" messages when we're in the foreground,
* and "explain" messages when we're in the foreground, and not "quiet".
*/
#include <unistd.h>
#include <syslog.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
#include "global.h"
static char truncated[] = " (truncated)";
static char msg[MAX_MSG + 1];
static int log_open = 0;
/* Number of complaints that we've seen */
int complaints = 0;
static void
xopenlog(void)
{
if (!log_open)
{
openlog(program_name, LOG_PID, SYSLOG_FACILITY);
log_open = 1;
}
}
void
xcloselog(void)
{
if (log_open) closelog();
log_open = 0;
}
static void
make_msg(const char *fmt, va_list args)
/* Construct the message string from its parts */
{
int len;
/* There's some confusion in the documentation about what vsnprintf
* returns when the buffer overflows. Hmmm... */
len = vsnprintf(msg, sizeof(msg), fmt, args);
if (len < 0) {
strncpy(msg, "(vsnprintf failed)", sizeof(msg));
msg[sizeof(msg) - 1] = '\0';
return;
}
if ((size_t) len >= sizeof(msg) - 1)
strcpy(msg + sizeof(msg) - sizeof(truncated), truncated);
}
static void
slog(int priority, const char *fmt, va_list args)
/* Log a message, described by "fmt" and "args", with the specified
* "priority". */
{
make_msg(fmt, args);
xopenlog();
syslog(priority, "%s", msg);
if (!in_background)
{
if (priority == EXPLAIN_LEVEL && !quiet)
fprintf(stderr, "%s\n", msg);
else if (priority == COMPLAIN_LEVEL)
fprintf(stderr, "%s: %s\n", program_name, msg);
}
}
static void
log_e(int priority, const char *fmt, va_list args)
/* Same as slog(), but also appends an error description corresponding
* to "errno". */
{
int saved_errno;
saved_errno = errno;
make_msg(fmt, args);
xopenlog();
syslog(priority, "%s: %s", msg, strerror(saved_errno));
if (!in_background)
{
if (priority == EXPLAIN_LEVEL && !quiet)
fprintf(stderr, "%s: %s\n", msg, strerror(saved_errno));
else if (priority == COMPLAIN_LEVEL)
fprintf(stderr, "%s: %s: %s\n",
program_name, msg, strerror(saved_errno));
}
}
void
explain(const char *fmt, ...)
/* Log an "explain" level message */
{
va_list args;
va_start(args, fmt);
slog(EXPLAIN_LEVEL, fmt, args);
va_end(args);
}
void
explain_e(const char *fmt, ...)
/* Log an "explain" level message, with an error description */
{
va_list args;
va_start(args, fmt);
log_e(EXPLAIN_LEVEL, fmt, args);
va_end(args);
}
void
complain(const char *fmt, ...)
/* Log a "complain" level message */
{
va_list args;
va_start(args, fmt);
slog(COMPLAIN_LEVEL, fmt, args);
va_end(args);
complaints += 1;
}
void
complain_e(const char *fmt, ...)
/* Log a "complain" level message, with an error description */
{
va_list args;
va_start(args, fmt);
log_e(COMPLAIN_LEVEL, fmt, args);
va_end(args);
complaints += 1;
}
void
die(const char *fmt, ...)
/* Log a "complain" level message, and exit */
{
va_list args;
va_start(args, fmt);
slog(COMPLAIN_LEVEL, fmt, args);
va_end(args);
if (getpid() == primary_pid) complain("Aborted");
exit(FAILURE_EXIT);
}
void
die_e(const char *fmt, ...)
/* Log a "complain" level message, with an error description, and exit */
{
va_list args;
va_start(args, fmt);
log_e(COMPLAIN_LEVEL, fmt, args);
va_end(args);
if (getpid() == primary_pid) complain("Aborted");
exit(FAILURE_EXIT);
}
#ifdef DEBUG
/* These are called through the Debug() and Debug_e() macros, defined
* in global.h */
void
xdebug(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
slog(DEBUG_LEVEL, fmt, args);
va_end(args);
}
void
xdebug_e(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
log_e(DEBUG_LEVEL, fmt, args);
va_end(args);
}
#endif /* DEBUG */

517
anacron/main.c Normal file
View File

@ -0,0 +1,517 @@
/*
Anacron - run commands periodically
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>
Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net>
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.
The GNU General Public License can also be found in the file
`COPYING' that comes with the Anacron source distribution.
*/
#include "config.h"
#include <time.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <locale.h>
#include "global.h"
#include "gregor.h"
#include "cronie_common.h"
pid_t primary_pid;
int day_now;
int year, month, day_of_month; /* date anacron started */
char *program_name;
char *anacrontab = NULL;
char *spooldir = NULL;
int serialize, force, update_only, now,
no_daemon, quiet, testing_only; /* command-line options */
char **job_args; /* vector of "job" command-line arguments */
int job_nargs; /* number of these */
char *defarg = "*";
int in_background; /* are we in the background? */
sigset_t old_sigmask; /* signal mask when started */
job_rec *first_job_rec;
env_rec *first_env_rec;
time_t start_sec; /* time anacron started */
static volatile int got_sigalrm, got_sigchld, got_sigusr1;
int running_jobs, running_mailers; /* , number of */
int range_start = -1;
int range_stop = -1;
int preferred_hour = -1;
static void
print_version(void)
{
printf("Anacron from project %s\n"
"Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>\n"
"Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>\n"
"Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net>\n"
"\n"
"Mail comments, suggestions and bug reports to <pasc@redellipse.net>."
"\n\n", PACKAGE_STRING);
}
static void
print_usage(void)
{
printf("Usage:\n");
printf(" %s [options] [job] ...\n", program_name);
printf(" %s -T [-t anacrontab-file]\n", program_name);
printf("\nOptions:\n");
printf(" -s Serialize execution of jobs\n");
printf(" -f Force execution of jobs, even before their time\n");
printf(" -n Run jobs with no delay, implies -s\n");
printf(" -d Don't fork to the background\n");
printf(" -q Suppress stderr messages, only applicable with -d\n");
printf(" -u Update the timestamps without actually running anything\n");
printf(" -V Print version information\n");
printf(" -h Print this message\n");
printf(" -t <file> Use alternative anacrontab\n");
printf(" -T Test an anacrontab\n");
printf(" -S <dir> Select a different spool directory\n");
printf("\nSee the anacron(8) manpage for more details.\n");
}
static void
parse_opts(int argc, char *argv[])
/* Parse command-line options */
{
int opt;
quiet = no_daemon = serialize = force = update_only = now = 0;
opterr = 0;
while ((opt = getopt(argc, argv, "sfundqt:TS:Vh")) != EOF)
{
switch (opt)
{
case 's':
serialize = 1;
break;
case 'f':
force = 1;
break;
case 'u':
update_only = 1;
break;
case 'n':
now = serialize = 1;
break;
case 'd':
no_daemon = 1;
break;
case 'q':
quiet = 1;
break;
case 't':
free(anacrontab);
anacrontab = strdup(optarg);
break;
case 'T':
testing_only = 1;
break;
case 'S':
free(spooldir);
spooldir = strdup(optarg);
break;
case 'V':
print_version();
exit(EXIT_SUCCESS);
case 'h':
print_usage();
exit(EXIT_SUCCESS);
case '?':
fprintf(stderr, "%s: invalid option: %c\n",
program_name, optopt);
fprintf(stderr, "type: `%s -h' for more information\n",
program_name);
exit(FAILURE_EXIT);
}
}
if (optind == argc)
{
/* no arguments. Equivalent to: `*' */
job_nargs = 1;
job_args = &defarg;
}
else
{
job_nargs = argc - optind;
job_args = argv + optind;
}
}
pid_t
xfork(void)
/* Like fork(), only never returns on failure */
{
pid_t pid;
pid = fork();
if (pid == -1) die_e("Can't fork");
return pid;
}
int
xopen(int fd, const char *file_name, int flags)
/* Like open, only it:
* a) never returns on failure, and
* b) if "fd" is non-negative, expect the file to open
* on file-descriptor "fd".
*/
{
int rfd;
rfd = open(file_name, flags);
if (fd >= 0 && rfd != fd)
die_e("Can't open %s on file-descriptor %d", file_name, fd);
else if (rfd < 0)
die_e("Can't open %s", file_name);
return rfd;
}
void
xclose(int fd)
/* Like close(), only doesn't return on failure */
{
if (close(fd)) die_e("Can't close file descriptor %d", fd);
}
static void
go_background(void)
/* Become a daemon. The foreground process exits successfully. */
{
pid_t pid;
/* stdin is already closed */
if (fclose(stdout)) die_e("Can't close stdout");
/* coverity[leaked_handle] fd 1 closed automatically */
xopen(1, "/dev/null", O_WRONLY);
if (fclose(stderr)) die_e("Can't close stderr");
/* coverity[leaked_handle] fd 2 closed automatically */
xopen(2, "/dev/null", O_WRONLY);
pid = xfork();
if (pid != 0)
{
/* parent */
exit(EXIT_SUCCESS);
}
else
{
/* child */
primary_pid = getpid();
if (setsid() == -1) die_e("setsid() error");
in_background = 1;
}
}
static void
handle_sigalrm(int unused ATTRIBUTE_UNUSED)
{
got_sigalrm = 1;
}
static void
handle_sigchld(int unused ATTRIBUTE_UNUSED)
{
got_sigchld = 1;
}
static void
handle_sigusr1(int unused ATTRIBUTE_UNUSED)
{
got_sigusr1 = 1;
}
static void
set_signal_handling(void)
/* We only use SIGALRM, SIGCHLD and SIGUSR1, and we unblock them only
* in wait_signal().
*/
{
sigset_t ss;
struct sigaction sa;
got_sigalrm = got_sigchld = got_sigusr1 = 0;
/* block SIGALRM, SIGCHLD and SIGUSR1 */
if (sigemptyset(&ss) ||
sigaddset(&ss, SIGALRM) ||
sigaddset(&ss, SIGCHLD) ||
sigaddset(&ss, SIGUSR1)) die_e("sigset error");
if (sigprocmask(SIG_BLOCK, &ss, NULL)) die_e ("sigprocmask error");
/* setup SIGALRM handler */
sa.sa_handler = handle_sigalrm;
sa.sa_mask = ss;
sa.sa_flags = 0;
if (sigaction(SIGALRM, &sa, NULL)) die_e("sigaction error");
/* setup SIGCHLD handler */
sa.sa_handler = handle_sigchld;
sa.sa_mask = ss;
sa.sa_flags = SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sa, NULL)) die_e("sigaction error");
/* setup SIGUSR1 handler */
sa.sa_handler = handle_sigusr1;
sa.sa_mask = ss;
sa.sa_flags = 0;
if (sigaction(SIGUSR1, &sa, NULL)) die_e("sigaction error");
}
static void
wait_signal(void)
/* Return after a signal is caught */
{
sigset_t ss;
if (sigprocmask(0, NULL, &ss)) die_e("sigprocmask error");
if (sigdelset(&ss, SIGALRM) ||
sigdelset(&ss, SIGCHLD) ||
sigdelset(&ss, SIGUSR1)) die_e("sigset error");
sigsuspend(&ss);
}
static void
wait_children(void)
/* Wait until we have no more children (of any kind) */
{
while (running_jobs > 0 || running_mailers > 0)
{
wait_signal();
if (got_sigchld) tend_children();
got_sigchld = 0;
if (got_sigusr1) explain("Received SIGUSR1");
got_sigusr1 = 0;
}
}
static void
orderly_termination(void)
/* Execution is diverted here, when we get SIGUSR1 */
{
explain("Received SIGUSR1");
got_sigusr1 = 0;
wait_children();
explain("Exited");
exit(EXIT_SUCCESS);
}
static void
xsleep(unsigned int n)
/* Sleep for n seconds, servicing SIGCHLDs and SIGUSR1s in the meantime.
* If n=0, return immediately.
*/
{
if (n == 0) return;
alarm(n);
do
{
wait_signal();
if (got_sigchld) tend_children();
got_sigchld = 0;
if (got_sigusr1) orderly_termination();
}
while (!got_sigalrm);
got_sigalrm = 0;
}
static void
wait_jobs(void)
/* Wait until there are no running jobs,
* servicing SIGCHLDs and SIGUSR1s in the meantime.
*/
{
while (running_jobs > 0)
{
wait_signal();
if (got_sigchld) tend_children();
got_sigchld = 0;
if (got_sigusr1) orderly_termination();
}
}
static void
record_start_time(void)
{
struct tm *tm_now;
start_sec = time(NULL);
tm_now = localtime(&start_sec);
year = tm_now->tm_year + 1900;
month = tm_now->tm_mon + 1;
day_of_month = tm_now->tm_mday;
day_now = day_num(year, month, day_of_month);
if (day_now == -1) die("Invalid date (this is really embarrassing)");
if (!update_only && !testing_only)
explain("Anacron started on %04d-%02d-%02d",
year, month, day_of_month);
}
static unsigned int
time_till(job_rec *jr)
/* Return the number of seconds that we have to wait until it's time
* to start job jr.
*/
{
time_t tj, tn;
if (now) return 0;
tn = time(NULL);
tj = start_sec + (time_t)jr->delay * 60;
if (tj < tn) return 0;
if (tj - tn > 3600*24)
{
explain("System time manipulation detected, job `%s' will run immediately",
jr->ident);
return 0;
}
return (unsigned int)(tj - tn);
}
static void
fake_jobs(void)
{
int j;
j = 0;
while (j < njobs)
{
fake_job(job_array[j]);
explain("Updated timestamp for job `%s' to %04d-%02d-%02d",
job_array[j]->ident, year, month, day_of_month);
j++;
}
}
static void
explain_intentions(void)
{
int j;
j = 0;
while (j < njobs)
{
if (now)
{
explain("Will run job `%s'", job_array[j]->ident);
}
else
{
explain("Will run job `%s' in %d min.",
job_array[j]->ident, job_array[j]->delay);
}
j++;
}
if (serialize && njobs > 0)
explain("Jobs will be executed sequentially");
}
int
main(int argc, char *argv[])
{
int j;
int cwd;
struct timeval tv;
struct timezone tz;
anacrontab = NULL;
spooldir = NULL;
setlocale(LC_ALL, "");
if (gettimeofday(&tv, &tz) != 0)
explain("Can't get exact time, failure.");
srandom((unsigned int)(getpid() + tv.tv_usec));
if((program_name = strrchr(argv[0], '/')) == NULL)
program_name = argv[0];
else
++program_name; /* move pointer to char after '/' */
parse_opts(argc, argv);
if (anacrontab == NULL)
anacrontab = strdup(ANACRONTAB);
if (spooldir == NULL)
spooldir = strdup(ANACRON_SPOOL_DIR);
if ((cwd = open ("./", O_RDONLY)) == -1) {
die_e ("Can't save current directory");
}
in_background = 0;
if (chdir(spooldir)) die_e("Can't chdir to %s", spooldir );
if (sigprocmask(0, NULL, &old_sigmask)) die_e("sigset error");
if (fclose(stdin)) die_e("Can't close stdin");
xopen(STDIN_FILENO, "/dev/null", O_RDONLY);
if (!no_daemon && !testing_only)
go_background();
else
primary_pid = getpid();
record_start_time();
read_tab(cwd);
close(cwd);
arrange_jobs();
if (testing_only)
{
if (complaints) exit (EXIT_FAILURE);
exit (EXIT_SUCCESS);
}
if (update_only)
{
fake_jobs();
exit(EXIT_SUCCESS);
}
explain_intentions();
set_signal_handling();
running_jobs = running_mailers = 0;
for(j = 0; j < njobs; ++j)
{
xsleep(time_till(job_array[j]));
if (serialize) wait_jobs();
launch_job(job_array[j]);
}
wait_children();
explain("Normal exit (%d job%s run)", njobs, njobs == 1 ? "" : "s");
exit(EXIT_SUCCESS);
}

92
anacron/matchrx.c Normal file
View File

@ -0,0 +1,92 @@
/*
Anacron - run commands periodically
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@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.
The GNU General Public License can also be found in the file
`COPYING' that comes with the Anacron source distribution.
*/
#include <stdio.h>
#include <regex.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "matchrx.h"
int
match_rx(const char *rx, char *string, unsigned int n_sub, /* char **substrings */...)
/* Return 1 if the regular expression "*rx" matches the string "*string",
* 0 if not, -1 on error.
* "Extended" regular expressions are used.
* Additionally, there should be "n_sub" "substrings" arguments. These,
* if not NULL, and if the match succeeds are set to point to the
* corresponding substrings of the regexp.
* The original string is changed, and the substrings must not overlap,
* or even be directly adjacent.
* This is not the most efficient, or elegant way of doing this.
*/
{
int r;
unsigned int n;
regex_t crx;
va_list va;
char **substring;
regmatch_t *sub_offsets;
sub_offsets = malloc(sizeof(regmatch_t) * (n_sub + 1));
if (sub_offsets == NULL)
return -1;
memset(sub_offsets, 0, sizeof(regmatch_t) * (n_sub + 1));
if (regcomp(&crx, rx, REG_EXTENDED)) {
free(sub_offsets);
return -1;
}
r = regexec(&crx, string, n_sub + 1, sub_offsets, 0);
if (r != 0 && r != REG_NOMATCH) {
free(sub_offsets);
return -1;
}
regfree(&crx);
if (r == REG_NOMATCH) {
free(sub_offsets);
return 0;
}
va_start(va, n_sub);
n = 1;
while (n <= n_sub)
{
substring = va_arg(va, char**);
if (substring != NULL)
{
if (sub_offsets[n].rm_so == -1) {
va_end(va);
free(sub_offsets);
return - 1;
}
*substring = string + sub_offsets[n].rm_so;
*(string + sub_offsets[n].rm_eo) = 0;
}
n++;
}
va_end(va);
free(sub_offsets);
return 1;
}

26
anacron/matchrx.h Normal file
View File

@ -0,0 +1,26 @@
/*
Anacron - run commands periodically
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@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.
The GNU General Public License can also be found in the file
`COPYING' that comes with the Anacron source distribution.
*/
int match_rx(const char *rx, char *string,
unsigned int n_sub, /* char **substrings */...);

451
anacron/readtab.c Normal file
View File

@ -0,0 +1,451 @@
/*
Anacron - run commands periodically
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@debian.org>
Copyright (C) 2004 Pascal Hakim <pasc@redellipse.net>
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.
The GNU General Public License can also be found in the file
`COPYING' that comes with the Anacron source distribution.
*/
/* /etc/anacrontab parsing, and job sorting
*/
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <obstack.h>
#include <limits.h>
#include <fnmatch.h>
#include <unistd.h>
#include <signal.h>
#include "global.h"
#include "matchrx.h"
static struct obstack input_o; /* holds input line */
static struct obstack tab_o; /* holds processed data read from anacrontab */
static FILE *tab;
job_rec **job_array;
int njobs; /* number of jobs to run */
static int jobs_read; /* number of jobs read */
static int line_num; /* current line in anacrontab */
static job_rec *last_job_rec; /* last job stored in memory, at the moment */
static env_rec *last_env_rec; /* last environment assignment stored */
static int random_number = 0;
/* some definitions for the obstack macros */
#define obstack_chunk_alloc xmalloc
#define obstack_chunk_free free
static void *
xmalloc (size_t size)
/* Just like standard malloc(), only never returns NULL. */
{
void * ptr;
ptr = malloc(size);
if (ptr == NULL)
die("Memory exhausted");
return ptr;
}
static int
conv2int(const char *s)
/* Return the int or -1 on over/under-flow
*/
{
long l;
errno = 0;
l = strtol(s, NULL, 10);
/* we use negative as error, so I am really returning unsigned int */
if (errno == ERANGE || l < 0 || l > INT_MAX) return - 1;
return (int)l;
}
static char *
read_tab_line (void)
/* Read one line and return a pointer to it.
Return NULL if no more lines.
*/
{
int c, prev=0;
if (feof(tab)) return NULL;
while (1)
{
c = getc(tab);
if ((c == '\n' && prev != '\\') || c == EOF)
{
if (0 != prev) obstack_1grow(&input_o, (char)prev);
break;
}
if (('\\' != prev || c != '\n') && 0 != prev && '\n' != prev) obstack_1grow(&input_o, (char)prev);
else if ('\n' == prev) obstack_1grow(&input_o, ' ');
prev = c;
}
if (ferror(tab)) die_e("Error reading %s", anacrontab);
obstack_1grow(&input_o, '\0');
return obstack_finish(&input_o);
}
static int
job_arg_num(const char *ident)
/* Return the command-line-argument number referring to this job-identifier.
* If it isn't specified, return -1.
*/
{
int i, r;
for (i = 0; i < job_nargs; i++)
{
r = fnmatch(job_args[i], ident, 0);
if (r == 0) return i;
if (r != FNM_NOMATCH) die("fnmatch() error");
}
return - 1;
}
static void
register_env(const char *env_var, const char *value)
/* Store the environment assignment "env_var"="value" */
{
env_rec *er;
int var_len, val_len;
var_len = (int)strlen(env_var);
val_len = (int)strlen(value);
if (!var_len) {
return;
}
er = obstack_alloc(&tab_o, sizeof(env_rec));
if (er == NULL) {
die_e("Cannot allocate memory.");
}
er->assign = obstack_alloc(&tab_o, var_len + 1 + val_len + 1);
if (er->assign == NULL) {
die_e("Cannot allocate memory.");
}
strcpy(er->assign, env_var);
er->assign[var_len] = '=';
strcpy(er->assign + var_len + 1, value);
er->assign[var_len + 1 + val_len] = 0;
if (last_env_rec != NULL) last_env_rec->next = er;
else first_env_rec = er;
last_env_rec = er;
Debug(("on line %d: %s", line_num, er->assign));
}
static void
register_job(const char *periods, const char *delays,
const char *ident, char *command)
/* Store a job definition */
{
int period, delay;
job_rec *jr;
int ident_len, command_len;
ident_len = (int)strlen(ident);
command_len = (int)strlen(command);
jobs_read++;
period = conv2int(periods);
delay = conv2int(delays);
if (period < 0 || delay < 0)
{
complain("%s: number out of range on line %d, skipping",
anacrontab, line_num);
return;
}
jr = obstack_alloc(&tab_o, sizeof(job_rec));
if (jr == NULL) {
die_e("Cannot allocate memory.");
}
jr->period = period;
jr->named_period = 0;
delay += random_number;
jr->delay = delay;
jr->tab_line = line_num;
jr->ident = obstack_alloc(&tab_o, ident_len + 1);
if (jr->ident == NULL) {
die_e("Cannot allocate memory.");
}
strcpy(jr->ident, ident);
jr->arg_num = job_arg_num(ident);
jr->command = obstack_alloc(&tab_o, command_len + 1);
if (jr->command == NULL) {
die_e("Cannot allocate memory.");
}
strcpy(jr->command, command);
jr->job_pid = jr->mailer_pid = 0;
if (last_job_rec != NULL) last_job_rec->next = jr;
else first_job_rec = jr;
last_job_rec = jr;
jr->prev_env_rec = last_env_rec;
jr->next = NULL;
Debug(("Read job - period=%d, delay=%d, ident=%s, command=%s",
jr->period, jr->delay, jr->ident, jr->command));
}
static void
register_period_job(const char *periods, const char *delays,
const char *ident, char *command)
/* Store a job definition with a named period */
{
int delay;
job_rec *jr;
int ident_len, command_len;
ident_len = (int)strlen(ident);
command_len = (int)strlen(command);
jobs_read++;
delay = conv2int(delays);
if (delay < 0)
{
complain("%s: number out of range on line %d, skipping",
anacrontab, line_num);
return;
}
jr = obstack_alloc(&tab_o, sizeof(job_rec));
if (jr == NULL) {
die_e("Cannot allocate memory.");
}
if (!strncmp ("@monthly", periods, 8)) {
jr->named_period = 1;
} else if (!strncmp("@yearly", periods, 7) || !strncmp("@annually", periods, 9) || !strncmp(/* backwards compat misspelling */"@annualy", periods, 8)) {
jr->named_period = 2;
} else if (!strncmp ("@daily", periods, 6)) {
jr->named_period = 3;
} else if (!strncmp ("@weekly", periods, 7)) {
jr->named_period = 4;
} else {
complain("%s: Unknown named period on line %d, skipping",
anacrontab, line_num);
}
jr->period = 0;
delay += random_number;
jr->delay = delay;
jr->tab_line = line_num;
jr->ident = obstack_alloc(&tab_o, ident_len + 1);
if (jr->ident == NULL) {
die_e("Cannot allocate memory.");
}
strcpy(jr->ident, ident);
jr->arg_num = job_arg_num(ident);
jr->command = obstack_alloc(&tab_o, command_len + 1);
if (jr->command == NULL) {
die_e("Cannot allocate memory.");
}
strcpy(jr->command, command);
jr->job_pid = jr->mailer_pid = 0;
if (last_job_rec != NULL) last_job_rec->next = jr;
else first_job_rec = jr;
last_job_rec = jr;
jr->prev_env_rec = last_env_rec;
jr->next = NULL;
Debug(("Read job - period %d, delay=%d, ident%s, command=%s",
jr->named_period, jr->delay, jr->ident, jr->command));
}
static long int
unbiased_rand(long int max)
{
long int rn;
long int divisor;
divisor = RAND_MAX / (max + 1);
do {
rn = random() / divisor;
} while (rn > max);
return rn;
}
static void
parse_tab_line(char *line)
{
int r;
char *env_var;
char *value;
char *periods;
char *delays;
char *ident;
char *command;
char *from;
char *to;
char *pref_hour;
/* an empty line? */
r = match_rx("^[ \t]*($|#)", line, 0);
if (r == -1) goto reg_err;
if (r)
{
Debug(("line %d empty", line_num));
return;
}
/* an environment assignment? */
r = match_rx("^[ \t]*([^ \t=]+)[ \t]*=(.*)$", line, 2,
&env_var, &value);
if (r == -1) goto reg_err;
if (r)
{
if (strncmp(env_var, "START_HOURS_RANGE", 17) == 0)
{
r = match_rx("^([[:digit:]]+)-([[:digit:]]+)$", value, 2, &from, &to);
if (r == -1) goto reg_err;
if (r == 0) goto reg_invalid;
range_start = atoi(from);
range_stop = atoi(to);
if (range_stop < range_start) {
range_start = 0; range_stop = 0;
goto reg_invalid;
}
Debug(("Jobs will start in the %02d:00-%02d:00 range.", range_start, range_stop));
}
else if (strncmp(env_var, "RANDOM_DELAY", 12) == 0) {
r = match_rx("^([[:digit:]]+)$", value, 0);
if (r == -1) goto reg_err;
if (r == 0) goto reg_invalid;
random_number = (int)unbiased_rand(atoi(value));
Debug(("Randomized delay set: %d", random_number));
}
else if (strncmp(env_var, "PREFERRED_HOUR", 14) == 0) {
r = match_rx("^([[:digit:]]+)$", value, 1, &pref_hour);
if (r == -1) goto reg_err;
if (r) {
preferred_hour = atoi(pref_hour);
if ((preferred_hour < 0) || (preferred_hour > 24)) {
preferred_hour = -1;
goto reg_invalid;
}
}
}
register_env(env_var, value);
return;
}
/* a job? */
r = match_rx("^[ \t]*([[:digit:]]+)[ \t]+([[:digit:]]+)[ \t]+"
"([^ \t/]+)[ \t]+([^ \t].*)$",
line, 4, &periods, &delays, &ident, &command);
if (r == -1) goto reg_err;
if (r)
{
register_job(periods, delays, ident, command);
return;
}
/* A period job? */
r = match_rx("^[ \t]*(@[^ \t]+)[ \t]+([[:digit:]]+)[ \t]+"
"([^ \t/]+)[ \t]+([^ \t].*)$",
line, 4, &periods, &delays, &ident, &command);
if (r == -1) goto reg_err;
if (r)
{
register_period_job(periods, delays, ident, command);
return;
}
reg_invalid:
complain("Invalid syntax in %s on line %d - skipping this line",
anacrontab, line_num);
return;
reg_err:
die("Regex error reading %s", anacrontab);
}
void
read_tab(int cwd)
/* Read the anacrontab file into memory */
{
char *tab_line;
first_job_rec = last_job_rec = NULL;
first_env_rec = last_env_rec = NULL;
jobs_read = 0;
line_num = 0;
/* Open the anacrontab file */
if (fchdir(cwd)) die_e("Can't chdir to original cwd");
tab = fopen(anacrontab, "r");
if (chdir(spooldir)) die_e("Can't chdir to %s", spooldir);
if (tab == NULL) die_e("Error opening %s", anacrontab);
/* Initialize the obstacks */
obstack_init(&input_o);
obstack_init(&tab_o);
while ((tab_line = read_tab_line()) != NULL)
{
line_num++;
parse_tab_line(tab_line);
obstack_free(&input_o, tab_line);
}
if (fclose(tab)) die_e("Error closing %s", anacrontab);
}
static int
execution_order(const job_rec **job1, const job_rec **job2)
/* Comparison function for sorting the jobs.
*/
{
int d;
d = (*job1)->arg_num - (*job2)->arg_num;
if (d != 0 && now) return d;
d = (*job1)->delay - (*job2)->delay;
if (d != 0) return d;
d = (*job1)->tab_line - (*job2)->tab_line;
return d;
}
void
arrange_jobs(void)
/* Make an array of pointers to jobs that are going to be executed,
* and arrange them in the order of execution.
* Also lock these jobs.
*/
{
job_rec *j;
j = first_job_rec;
njobs = 0;
while (j != NULL)
{
if (j->arg_num != -1 && (update_only || testing_only || consider_job(j)))
{
njobs++;
obstack_grow(&tab_o, &j, sizeof(j));
}
j = j->next;
}
job_array = obstack_finish(&tab_o);
/* sort the jobs */
qsort(job_array, (size_t)njobs, sizeof(*job_array),
(int (*)(const void *, const void *))execution_order);
}

424
anacron/runjob.c Normal file
View File

@ -0,0 +1,424 @@
/*
Anacron - run commands periodically
Copyright (C) 1998 Itai Tzur <itzur@actcom.co.il>
Copyright (C) 1999 Sean 'Shaleh' Perry <shaleh@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.
The GNU General Public License can also be found in the file
`COPYING' that comes with the Anacron source distribution.
*/
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include "global.h"
#include "cronie_common.h"
#include <langinfo.h>
static int
temp_file(job_rec *jr)
/* Open a temporary file and return its file descriptor */
{
char *dir;
char template[PATH_MAX+1];
int fdin = -1;
int fdout;
int len;
dir = getenv("TMPDIR");
if (dir == NULL || *dir == '\0')
dir = P_tmpdir;
len = snprintf(template, sizeof(template), "%s/$anacronXXXXXX", dir);
if (len < 0)
die_e("snprintf failed");
else if ((size_t) len >= sizeof(template))
die_e("TMPDIR too long");
fdout = mkstemp(template);
if (fdout == -1) die_e("Can't open temporary file for writing");
fdin = open(template, O_RDONLY, S_IRUSR | S_IWUSR);
if (fdin == -1) die_e("Can't open temporary file for reading");
if (unlink(template)) die_e("Can't unlink temporary file");
fcntl(fdout, F_SETFD, FD_CLOEXEC); /* set close-on-exec flag */
fcntl(fdin, F_SETFD, FD_CLOEXEC); /* set close-on-exec flag */
jr->input_fd = fdin;
jr->output_fd = fdout;
return fdout;
}
static off_t
file_size(int fd)
/* Return the size of temporary file fd */
{
struct stat st;
if (fstat(fd, &st)) die_e("Can't fstat temporary file");
return st.st_size;
}
static char *
username(void)
{
struct passwd *ps;
static char *user;
if (user)
return user;
ps = getpwuid(geteuid());
if (ps == NULL || ps->pw_name == NULL) die_e("getpwuid() error");
user = strdup(ps->pw_name);
if (user == NULL) die_e("memory allocation error");
return user;
}
static void
xputenv(const char *s)
{
char *name = NULL, *val = NULL;
char *eq_ptr;
size_t eq_index;
if (s == NULL) {
die_e("Invalid environment string");
}
eq_ptr = strchr(s, '=');
if (eq_ptr == NULL) {
die_e("Invalid environment string");
}
eq_index = (size_t) (eq_ptr - s);
name = malloc((eq_index + 1) * sizeof(char));
if (name == NULL) {
die_e("Not enough memory to set the environment");
}
val = malloc((strlen(s) - eq_index) * sizeof(char));
if (val == NULL) {
die_e("Not enough memory to set the environment");
}
strncpy(name, s, eq_index);
name[eq_index] = '\0';
strcpy(val, s + eq_index + 1);
if (setenv(name, val, 1)) {
die_e("Can't set the environment");
}
free(name);
free(val);
return;
}
static void
setup_env(const job_rec *jr)
/* Setup the environment for the job according to /etc/anacrontab */
{
env_rec *er;
er = first_env_rec;
if (er == NULL || jr->prev_env_rec == NULL) return;
xputenv(er->assign);
while (er != jr->prev_env_rec)
{
er = er->next;
xputenv(er->assign);
}
}
static void
run_job(const job_rec *jr)
/* This is called to start the job, after the fork */
{
/* setup stdout and stderr */
xclose(1);
xclose(2);
if (dup2(jr->output_fd, 1) != 1 || dup2(jr->output_fd, 2) != 2)
die_e("dup2() error"); /* dup2 also clears close-on-exec flag */
in_background = 0; /* now, errors will be mailed to the user */
if (chdir("/")) die_e("Can't chdir to '/'");
if (sigprocmask(SIG_SETMASK, &old_sigmask, NULL))
die_e("sigprocmask error");
xcloselog();
execl("/bin/sh", "/bin/sh", "-c", jr->command, (char *)NULL);
die_e("execl() error");
}
static void
xwrite(int fd, const char *string)
/* Write (using write()) the string "string" to temporary file "fd".
* Don't return on failure */
{
if (write(fd, string, strlen(string)) == -1)
die_e("Can't write to temporary file");
}
static int
xwait(pid_t pid , int *status)
/* Check if child process "pid" has finished. If it has, return 1 and its
* exit status in "*status". If not, return 0.
*/
{
pid_t r;
r = waitpid(pid, status, WNOHANG);
if (r == -1) die_e("waitpid() error");
if (r == 0) return 0;
return 1;
}
static void
launch_mailer(job_rec *jr)
{
pid_t pid;
struct stat buf;
if (jr->mailto == NULL)
{
explain("Empty MAILTO set, not mailing output");
return;
}
/* Check that we have a way of sending mail. */
if(stat(SENDMAIL, &buf))
{
complain("Can't find sendmail at %s, not mailing output", SENDMAIL);
return;
}
pid = xfork();
if (pid == 0)
{
/* child */
in_background = 1;
/* set stdin to the job's output */
xclose(STDIN_FILENO);
if (dup2(jr->input_fd, STDIN_FILENO) != 0) die_e("Can't dup2()");
if (lseek(STDIN_FILENO, 0, SEEK_SET) != 0) die_e("Can't lseek()");
if (sigprocmask(SIG_SETMASK, &old_sigmask, NULL))
die_e("sigprocmask error");
xcloselog();
/* Ensure stdout/stderr are sane before exec-ing sendmail */
/* coverity[leaked_handle] STDOUT closed automatically */
xclose(STDOUT_FILENO); xopen(STDOUT_FILENO, "/dev/null", O_WRONLY);
/* coverity[leaked_handle] STDERR closed automatically */
xclose(STDERR_FILENO); xopen(STDERR_FILENO, "/dev/null", O_WRONLY);
xclose(jr->output_fd);
/* Ensure stdin is not appendable ... ? */
/* fdflags = fcntl(0, F_GETFL); fdflags &= ~O_APPEND; */
/* fcntl(0, F_SETFL, fdflags ); */
/* Here, I basically mirrored the way /usr/sbin/sendmail is called
* by cron on a Debian system, except for the "-oem" and "-or0s"
* options, which don't seem to be appropriate here.
* Hopefully, this will keep all the MTAs happy. */
execl(SENDMAIL, SENDMAIL, "-FAnacron", "-odi",
jr->mailto, (char *)NULL);
die_e("Can't exec " SENDMAIL);
}
/* parent */
/* record mailer pid */
jr->mailer_pid = pid;
running_mailers++;
}
static void
tend_mailer(job_rec *jr, int status)
{
if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
complain("Tried to mail output of job `%s', "
"but mailer process (" SENDMAIL ") exited with status %d",
jr->ident, WEXITSTATUS(status));
else if (!WIFEXITED(status) && WIFSIGNALED(status))
complain("Tried to mail output of job `%s', "
"but mailer process (" SENDMAIL ") got signal %d",
jr->ident, WTERMSIG(status));
else if (!WIFEXITED(status) && !WIFSIGNALED(status))
complain("Tried to mail output of job `%s', "
"but mailer process (" SENDMAIL ") terminated abnormally"
, jr->ident);
jr->mailer_pid = 0;
running_mailers--;
}
void
launch_job(job_rec *jr)
{
pid_t pid;
int fd;
char hostname[512];
char *mailto;
char *mailfrom;
char mailto_expanded[MAX_EMAILSTR];
char mailfrom_expanded[MAX_EMAILSTR];
/* get hostname */
if (gethostname(hostname, 512)) {
strcpy (hostname,"unknown machine");
}
setup_env(jr);
/* Get the destination email address if set, or current user otherwise */
mailto = getenv("MAILTO");
if (mailto == NULL) {
mailto = username();
}
else {
if (expand_envvar(mailto, mailto_expanded, sizeof(mailto_expanded))) {
mailto = mailto_expanded;
}
else {
complain("The environment variable 'MAILTO' could not be expanded. The non-expanded value will be used.");
}
}
/* Get the source email address if set, or current user otherwise */
mailfrom = getenv("MAILFROM");
if (mailfrom == NULL) {
mailfrom = username();
}
else {
if (expand_envvar(mailfrom, mailfrom_expanded, sizeof(mailfrom_expanded))) {
mailfrom = mailfrom_expanded;
}
else {
complain("The environment variable 'MAILFROM' could not be expanded. The non-expanded value will be used.");
}
}
/* create temporary file for stdout and stderr of the job */
temp_file(jr); fd = jr->output_fd;
/* write mail header */
xwrite(fd, "From: ");
xwrite(fd, "Anacron <");
xwrite(fd, mailfrom);
xwrite(fd, ">\n");
xwrite(fd, "To: ");
xwrite(fd, mailto);
xwrite(fd, "\n");
xwrite(fd, "MIME-Version: 1.0\n");
xwrite(fd, "Content-Type: text/plain; charset=\"");
xwrite(fd, nl_langinfo(CODESET));
xwrite(fd, "\"\n");
xwrite(fd, "Content-Transfer-Encoding: 8bit\n");
xwrite(fd, "Subject: Anacron job '");
xwrite(fd, jr->ident);
xwrite(fd, "' on ");
xwrite(fd, hostname);
xwrite(fd, "\n\n");
if (*mailto == '\0')
jr->mailto = NULL;
else
/* ugly but works without strdup() */
jr->mailto = mailto;
jr->mail_header_size = file_size(fd);
pid = xfork();
if (pid == 0)
{
/* child */
in_background = 1;
run_job(jr);
/* execution never gets here */
}
/* parent */
explain("Job `%s' started", jr->ident);
jr->job_pid = pid;
running_jobs++;
}
static void
tend_job(job_rec *jr, int status)
/* Take care of a finished job */
{
int mail_output;
const char *m;
update_timestamp(jr);
unlock(jr);
if (file_size(jr->output_fd) > jr->mail_header_size) mail_output = 1;
else mail_output = 0;
m = mail_output ? " (produced output)" : "";
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
explain("Job `%s' terminated%s", jr->ident, m);
else if (WIFEXITED(status))
explain("Job `%s' terminated (exit status: %d)%s",
jr->ident, WEXITSTATUS(status), m);
else if (WIFSIGNALED(status))
complain("Job `%s' terminated due to signal %d%s",
jr->ident, WTERMSIG(status), m);
else /* is this possible? */
complain("Job `%s' terminated abnormally%s", jr->ident, m);
jr->job_pid = 0;
running_jobs--;
if (mail_output) launch_mailer(jr);
xclose(jr->output_fd);
xclose(jr->input_fd);
}
void
tend_children(void)
/* This is called whenever we get a SIGCHLD.
* Takes care of zombie children.
*/
{
int j;
int status;
j = 0;
while (j < njobs)
{
if (job_array[j]->mailer_pid != 0 &&
xwait(job_array[j]->mailer_pid, &status))
tend_mailer(job_array[j], status);
if (job_array[j]->job_pid != 0 &&
xwait(job_array[j]->job_pid, &status))
tend_job(job_array[j], status);
j++;
}
}

348
compile Executable file
View File

@ -0,0 +1,348 @@
#! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# 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, 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, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
nl='
'
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent tools from complaining about whitespace usage.
IFS=" "" $nl"
file_conv=
# func_file_conv build_file lazy
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts. If the determined conversion
# type is listed in (the comma separated) LAZY, no conversion will
# take place.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv/,$2, in
*,$file_conv,*)
;;
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_cl_dashL linkdir
# Make cl look for libraries in LINKDIR
func_cl_dashL ()
{
func_file_conv "$1"
if test -z "$lib_path"; then
lib_path=$file
else
lib_path="$lib_path;$file"
fi
linker_opts="$linker_opts -LIBPATH:$file"
}
# func_cl_dashl library
# Do a library search-path lookup for cl
func_cl_dashl ()
{
lib=$1
found=no
save_IFS=$IFS
IFS=';'
for dir in $lib_path $LIB
do
IFS=$save_IFS
if $shared && test -f "$dir/$lib.dll.lib"; then
found=yes
lib=$dir/$lib.dll.lib
break
fi
if test -f "$dir/$lib.lib"; then
found=yes
lib=$dir/$lib.lib
break
fi
if test -f "$dir/lib$lib.a"; then
found=yes
lib=$dir/lib$lib.a
break
fi
done
IFS=$save_IFS
if test "$found" != yes; then
lib=$lib.lib
fi
}
# func_cl_wrapper cl arg...
# Adjust compile command to suit cl
func_cl_wrapper ()
{
# Assume a capable shell
lib_path=
shared=:
linker_opts=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
eat=1
case $2 in
*.o | *.[oO][bB][jJ])
func_file_conv "$2"
set x "$@" -Fo"$file"
shift
;;
*)
func_file_conv "$2"
set x "$@" -Fe"$file"
shift
;;
esac
;;
-I)
eat=1
func_file_conv "$2" mingw
set x "$@" -I"$file"
shift
;;
-I*)
func_file_conv "${1#-I}" mingw
set x "$@" -I"$file"
shift
;;
-l)
eat=1
func_cl_dashl "$2"
set x "$@" "$lib"
shift
;;
-l*)
func_cl_dashl "${1#-l}"
set x "$@" "$lib"
shift
;;
-L)
eat=1
func_cl_dashL "$2"
;;
-L*)
func_cl_dashL "${1#-L}"
;;
-static)
shared=false
;;
-Wl,*)
arg=${1#-Wl,}
save_ifs="$IFS"; IFS=','
for flag in $arg; do
IFS="$save_ifs"
linker_opts="$linker_opts $flag"
done
IFS="$save_ifs"
;;
-Xlinker)
eat=1
linker_opts="$linker_opts $2"
;;
-*)
set x "$@" "$1"
shift
;;
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
func_file_conv "$1"
set x "$@" -Tp"$file"
shift
;;
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
func_file_conv "$1" mingw
set x "$@" "$file"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -n "$linker_opts"; then
linker_opts="-link$linker_opts"
fi
exec "$@" $linker_opts
exit 1
}
eat=
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand '-c -o'.
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file 'INSTALL'.
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "compile $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \
icl | *[/\\]icl | icl.exe | *[/\\]icl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
ofile=
cfile=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
# So we strip '-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no '-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# '.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

1476
config.guess vendored Executable file

File diff suppressed because it is too large Load Diff

249
config.h.in Normal file
View File

@ -0,0 +1,249 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* if you have a tm_gmtoff member in struct tm */
#undef CAPITALIZE_FOR_PS
/* Code will be built with debug info. */
#undef DEBUGGING
/* default editor */
#undef EDITOR
/* Define if you want system crontab. */
#undef ENABLE_SYSCRONTAB
/* Define to 1 if you have the <dirent.h> header file. */
#undef HAVE_DIRENT_H
/* Define to 1 if you have the `fchgrp' function. */
#undef HAVE_FCHGRP
/* Define to 1 if you have the `fchown' function. */
#undef HAVE_FCHOWN
/* Define to 1 if you have the `fcntl' function. */
#undef HAVE_FCNTL
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define to 1 if you have the `flock' function. */
#undef HAVE_FLOCK
/* Define to 1 if you have the <getopt.h> header file. */
#undef HAVE_GETOPT_H
/* Define to 1 if you have the `getseuserbyname' function. */
#undef HAVE_GETSEUSERBYNAME
/* Define to 1 if you have the `get_default_context_with_level' function. */
#undef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL
/* Define to 1 if you have the <glob.h> header file. */
#undef HAVE_GLOB_H
/* Define to 1 if you have the `inotify_add_watch' function. */
#undef HAVE_INOTIFY_ADD_WATCH
/* Define to 1 if you have the `inotify_init' function. */
#undef HAVE_INOTIFY_INIT
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the `pam' library (-lpam). */
#undef HAVE_LIBPAM
/* Define to 1 if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
/* Define to 1 if you have the `lockf' function. */
#undef HAVE_LOCKF
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have the `pam_getenvlist' function. */
#undef HAVE_PAM_GETENVLIST
/* Define to 1 if you have the <pam/pam_appl.h> header file. */
#undef HAVE_PAM_PAM_APPL_H
/* Define to 1 if you have the `pam_putenv' function. */
#undef HAVE_PAM_PUTENV
/* Define to 1 if you have the <paths.h> header file. */
#undef HAVE_PATHS_H
/* Define to 1 if you have the <pty.h> header file. */
#undef HAVE_PTY_H
/* Define to 1 if you have the <security/pam_appl.h> header file. */
#undef HAVE_SECURITY_PAM_APPL_H
/* Define to 1 if you have the <selinux/selinux.h> header file. */
#undef HAVE_SELINUX_SELINUX_H
/* Define to 1 if you have the <stddef.h> header file. */
#undef HAVE_STDDEF_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if `tm_gmtoff' is a member of `struct tm'. */
#undef HAVE_STRUCT_TM_TM_GMTOFF
/* Define to 1 if you have the <sys/audit.h> header file. */
#undef HAVE_SYS_AUDIT_H
/* Define to 1 if you have the <sys/cdefs.h> header file. */
#undef HAVE_SYS_CDEFS_H
/* Define to 1 if you have the <sys/inotify.h> header file. */
#undef HAVE_SYS_INOTIFY_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/stream.h> header file. */
#undef HAVE_SYS_STREAM_H
/* Define to 1 if you have the <sys/stropts.h> header file. */
#undef HAVE_SYS_STROPTS_H
/* Define to 1 if you have the <sys/timers.h> header file. */
#undef HAVE_SYS_TIMERS_H
/* Define to 1 if you have the <sys/time.h> header file. */
#undef HAVE_SYS_TIME_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <time.h> header file. */
#undef HAVE_TIME_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the <util.h> header file. */
#undef HAVE_UTIL_H
/* Define to 1 if you have the <utime.h> header file. */
#undef HAVE_UTIME_H
/* There will be path to sendmail. */
#undef MAILARG
/* -i = don't terminate on "." by itself -Fx = Set full-name of sender -odi =
Option Deliverymode Interactive -oem = Option Errors Mailedtosender -oi =
Ignore "." alone on a line -t = Get recipient from headers -f %s = Envelope
sender address -d = undocumented but common flag. */
#undef MAILFMT
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Using syslog for log messages. */
#undef SYSLOG
/* Define to 1 if your <sys/time.h> declares `struct tm'. */
#undef TM_IN_SYS_TIME
/* Enable extensions on AIX 3, Interix. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Enable GNU extensions on systems that have them. */
#ifndef _GNU_SOURCE
# undef _GNU_SOURCE
#endif
/* Enable threading extensions on Solaris. */
#ifndef _POSIX_PTHREAD_SEMANTICS
# undef _POSIX_PTHREAD_SEMANTICS
#endif
/* Enable extensions on HP NonStop. */
#ifndef _TANDEM_SOURCE
# undef _TANDEM_SOURCE
#endif
/* Enable general extensions on Solaris. */
#ifndef __EXTENSIONS__
# undef __EXTENSIONS__
#endif
/* Version number of package */
#undef VERSION
/* Define if you want Audit trails. */
#undef WITH_AUDIT
/* Define if you want inotify support. */
#undef WITH_INOTIFY
/* Define if you want to enable PAM support */
#undef WITH_PAM
/* Define if you want SELinux support. */
#undef WITH_SELINUX
/* Define to 1 if on MINIX. */
#undef _MINIX
/* Define to 2 if the system does not provide POSIX.1 features except with
this defined. */
#undef _POSIX_1_SOURCE
/* Define to 1 if you need to in order for `stat' and other things to work. */
#undef _POSIX_SOURCE
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to `int' if <sys/types.h> doesn't define. */
#undef gid_t
/* Define to `int' if <sys/types.h> does not define. */
#undef mode_t
/* Define to `long int' if <sys/types.h> does not define. */
#undef off_t
/* Define to `int' if <sys/types.h> does not define. */
#undef pid_t
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t
/* Define to `int' if <sys/types.h> doesn't define. */
#undef uid_t

1833
config.sub vendored Executable file

File diff suppressed because it is too large Load Diff

6909
configure vendored Executable file

File diff suppressed because it is too large Load Diff

273
configure.ac Normal file
View File

@ -0,0 +1,273 @@
AC_INIT([cronie],[1.6.1])
AC_CONFIG_HEADERS([config.h])
AC_PREREQ([2.64])
AM_INIT_AUTOMAKE([subdir-objects])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])],
[AC_SUBST([AM_DEFAULT_VERBOSITY], [1])])
AC_CANONICAL_HOST
dnl Checks for programs.
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_LN_S
dnl Check for _GNU_SOURCE
AC_USE_SYSTEM_EXTENSIONS
AC_CHECK_HEADERS( \
dirent.h \
fcntl.h \
getopt.h \
glob.h \
limits.h \
paths.h \
pty.h \
selinux/selinux.h \
stddef.h \
stdint.h \
sys/audit.h \
sys/inotify.h \
sys/stat.h \
sys/stream.h \
sys/stropts.h \
sys/time.h \
sys/timers.h \
sys/types.h \
sys/cdefs.h \
time.h \
unistd.h \
util.h \
utime.h \
)
AC_CHECK_FUNCS( \
fcntl \
lockf \
flock \
fchown \
fchgrp \
)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C_CONST
AC_TYPE_UID_T
AC_TYPE_MODE_T
AC_TYPE_OFF_T
AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_STRUCT_TM
AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[#include <time.h>])
dnl Checking for programs
AC_ARG_WITH([editor],
[AS_HELP_STRING([--with-editor=EDITOR],[path to default editor])],
[editor_defined="$with_editor"],
[editor_defined="no"])
AS_IF([test "x$editor_defined" = "xno"], [
AC_PATH_PROG([editor_defined], [vi], [/usr/bin/vi])
])
AC_DEFINE_UNQUOTED([EDITOR], ["$editor_defined"], [default editor])
AC_MSG_CHECKING(username to run under)
AC_ARG_WITH(daemon_username,
[AS_HELP_STRING([--with-daemon_username=DAEMON_USERNAME], [Username to run under (default daemon) ])],
[ case "$withval" in
no)
AC_MSG_ERROR(Need DAEMON_USERNAME.)
;;
yes)
DAEMON_USERNAME=daemon
AC_MSG_RESULT(daemon)
;;
*)
DAEMON_USERNAME="$withval";
AC_MSG_RESULT($withval)
;;
esac ],
DAEMON_USERNAME=daemon
AC_MSG_RESULT(daemon)
)
AC_SUBST(DAEMON_USERNAME)
AC_MSG_CHECKING(groupname to run under)
AC_ARG_WITH(daemon_groupname,
[AS_HELP_STRING([--with-daemon_groupname=DAEMON_GROUPNAME], [Groupname to run under (default daemon) ])],
[ case "$withval" in
no)
AC_MSG_ERROR(Need DAEMON_GROUPNAME.)
;;
yes)
DAEMON_GROUPNAME=daemon
AC_MSG_RESULT(daemon)
;;
*)
DAEMON_GROUPNAME="$withval";
AC_MSG_RESULT($withval)
;;
esac ],
DAEMON_GROUPNAME=daemon
AC_MSG_RESULT(daemon)
)
AC_SUBST(DAEMON_GROUPNAME)
# Check whether inotify is accepted
AC_ARG_WITH(inotify,
[AS_HELP_STRING([--with-inotify], [ Enable inotify support])],
[ if test "x$withval" != "xno" ; then
AC_DEFINE(WITH_INOTIFY,1,[Define if you want inotify support.])
AC_CHECK_HEADER([sys/inotify.h], , AC_MSG_ERROR(Inotify support requires sys/inotify.h header))
AC_CHECK_FUNCS(inotify_init inotify_add_watch)
fi
]
)
AC_ARG_ENABLE(pie,CRONIE_HELP_STRING(--enable-pie,Build cronie as a Position Independent Executable))
if test "x$enable_pie" = xyes; then
CFLAGS="$CFLAGS -fPIE -DPIE"
LDFLAGS="$LDFLAGS -pie"
fi
AC_ARG_ENABLE(relro,CRONIE_HELP_STRING(--enable-relro,Build cronie with relro flag))
if test "x$enable_relro" = xyes; then
LDFLAGS="$LDFLAGS -Wl,-z,relro -Wl,-z,now"
fi
AC_ARG_ENABLE(bsd, BSD_STRING(--enable-bsd,Build cronie with BSD specific parts))
# Check whether user wants SELinux support
SELINUX_MSG="no"
LIBSELINUX=""
AC_ARG_WITH(selinux,
[AS_HELP_STRING([--with-selinux], [Enable SELinux support])],
[ if test "x$withval" != "xno" ; then
saved_LIBS="$LIBS"
AC_DEFINE(WITH_SELINUX,1,[Define if you want SELinux support.])
SELINUX_MSG="yes"
AC_CHECK_HEADER([selinux/selinux.h], ,AC_MSG_ERROR(SELinux support requires selinux.h header))
AC_CHECK_LIB(selinux, setexeccon, [ LIBSELINUX="-lselinux" ],
AC_MSG_ERROR(SELinux support requires libselinux library))
AC_CHECK_FUNCS(getseuserbyname get_default_context_with_level)
LIBS="$saved_LIBS"
AC_SUBST(LIBSELINUX)
fi ]
)
AC_ARG_WITH(pam, [AS_HELP_STRING([--with-pam], [Build with PAM support])])
AC_ARG_ENABLE(pam, [AS_HELP_STRING([--enable-pam], [Alias for --with-pam])])
# Check that with_pam and enable_pam are consistent.
# If neither one is set, the default is "no."
if test -z "$with_pam"; then
with_pam=${enable_pam:-no}
elif test -n "$enable_pam" && test "$with_pam" != "$enable_pam"; then
AC_MSG_ERROR(
[Contradicting --with/without-pam and --enable/disable-pam options.])
fi
AM_CONDITIONAL([PAM], [test "$with_pam" != no])
if test "$with_pam" != no; then
AC_DEFINE(WITH_PAM, 1, [Define if you want to enable PAM support])
pam_appl_h_found=no
AC_CHECK_HEADERS([pam/pam_appl.h security/pam_appl.h],
[pam_appl_h_found=yes])
test "$pam_appl_h_found" = yes ||
AC_MSG_ERROR([PAM headers not found])
saved_LIBS="$LIBS"
AC_CHECK_LIB([dl], [dlopen], [libdl_found=yes], [libdl_found=no])
AC_CHECK_LIB(pam, pam_set_item, , AC_MSG_ERROR([*** libpam missing]))
AC_CHECK_FUNCS([pam_getenvlist pam_putenv])
LIBS="$saved_LIBS"
case $libdl_found:" $LIBS " in #(
*" -ldl "*) LIBPAM= ;; #(
yes:*) LIBPAM=-ldl ;; # libdl found, but is not in $LIBS
esac
AC_SUBST([LIBPAM], ["-lpam $LIBPAM"])
fi
AC_DEFINE(DEBUGGING,1,[Code will be built with debug info.])
AC_DEFINE(MAILARG,"/usr/sbin/sendmail",[There will be path to sendmail.])
AC_DEFINE(MAILFMT,"%s -FCronDaemon -i -odi -oem -oi -t -f %s",
[-i = don't terminate on "." by itself
-Fx = Set full-name of sender
-odi = Option Deliverymode Interactive
-oem = Option Errors Mailedtosender
-oi = Ignore "." alone on a line
-t = Get recipient from headers
-f %s = Envelope sender address
-d = undocumented but common flag.])
AC_DEFINE(SYSLOG,1,[Using syslog for log messages.])
AC_DEFINE(CAPITALIZE_FOR_PS, 1, [if you have a tm_gmtoff member in struct tm])
# Check whether user wants Linux audit support
AC_ARG_WITH(audit,
[AS_HELP_STRING([--with-audit], [Enable audit trails])],
[ if test "x$withval" != "xno" ; then
saved_LIBS="$LIBS"
AC_DEFINE(WITH_AUDIT,1,[Define if you want Audit trails.])
AC_CHECK_HEADER([libaudit.h], ,AC_MSG_ERROR(Audit trails requires libaudit.h header))
AC_CHECK_LIB(audit, audit_open, [ LIBAUDIT="-laudit" ],
AC_MSG_ERROR(Audit support needs audit libraries.))
LIBS="$saved_LIBS"
AC_SUBST(LIBAUDIT)
fi ]
)
AC_ARG_ENABLE(syscrontab,
[AS_HELP_STRING([--enable-syscrontab], [Build cronie with system crontab enabled.])],
[ if test "x$enableval" != xno; then
AC_DEFINE(ENABLE_SYSCRONTAB,1,[Define if you want system crontab.])
fi ], [AC_DEFINE(ENABLE_SYSCRONTAB,1,[Define if you want system crontab.])]
)
dnl CRONIE_VAR_DEFAULT (VAR, DESCRIPTION, DEFAULT)
dnl --------------------------------------------
AC_DEFUN([CRONIE_CONF_VAR],
[AC_ARG_VAR([$1], [$2 @<:@$3@:>@])
if test "$$1" = ""; then
$1='$3'
fi
])
AC_DEFUN([ANACRON_CONF_VAR],
[AC_ARG_VAR([$1], [$2 @<:@$3@:>@])
if test "$$1" = ""; then
$1='$3'
fi
])
CRONIE_CONF_VAR([SYSCRONTAB], [the current working directory of the running daemon], [${sysconfdir}/crontab])
CRONIE_CONF_VAR([SYS_CROND_DIR], [the current working directory of the running daemon], [${sysconfdir}/cron.d])
CRONIE_CONF_VAR([SPOOL_DIR], [the directory where all the user cron tabs reside], [${localstatedir}/spool/cron])
AC_ARG_ENABLE([anacron], [AS_HELP_STRING([--disable-anacron], [Do not build anacron.])], [], [enable_anacron=yes])
AM_CONDITIONAL([ANACRON], [test "$enable_anacron" = yes])
if test "$enable_anacron" != no; then
ANACRON_CONF_VAR([ANACRON_SPOOL_DIR],[The path for anacron locks.],[${localstatedir}/spool/anacron])
ANACRON_CONF_VAR([ANACRONTAB],[The anacron table for regular jobs.],[${sysconfdir}/anacrontab])
dnl obstack.h is part of GLIBC and may not be present on other systems,
dnl eg. musl and AIX. There, we static link in our own copy.
AC_CHECK_HEADER(obstack.h, [have_obstack=yes], [have_obstack=no], [])
fi
AM_CONDITIONAL([NEED_OBSTACK], [test "$have_obstack" = no])
AS_IF([test "$enable_anacron" != no && test "$have_obstack" = no], [
CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/obstack"
])
AM_CONDITIONAL(HAS_RUNSTATE, [test x$runstatedir != x])
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

25
contrib/0anacron Normal file
View File

@ -0,0 +1,25 @@
#!/bin/sh
# Check whether 0anacron was run today already
if test -r /var/spool/anacron/cron.daily; then
day=`cat /var/spool/anacron/cron.daily`
fi
if [ `date +%Y%m%d` = "$day" ]; then
exit 0
fi
# Do not run jobs when on battery power
online=1
for psupply in /sys/class/power_supply/* ; do
if [ `cat "$psupply/type" 2>/dev/null`x = Mainsx ] && [ -f "$psupply/online" ]; then
if [ `cat "$psupply/online" 2>/dev/null`x = 1x ]; then
online=1
break
else
online=0
fi
fi
done
if [ $online = 0 ]; then
exit 0
fi
/usr/sbin/anacron -s

5
contrib/0hourly Normal file
View File

@ -0,0 +1,5 @@
# Run the hourly jobs
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
01 * * * * root run-parts /etc/cron.hourly

16
contrib/anacrontab Normal file
View File

@ -0,0 +1,16 @@
# /etc/anacrontab: configuration file for anacron
# See anacron(8) and anacrontab(5) for details.
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22
#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly

15
contrib/cronie.systemd Normal file
View File

@ -0,0 +1,15 @@
[Unit]
Description=Command Scheduler
After=auditd.service nss-user-lookup.target systemd-user-sessions.service time-sync.target ypbind.service autofs.service
[Service]
EnvironmentFile=/etc/sysconfig/crond
ExecStart=/usr/sbin/crond -n $CRONDARGS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=30s
[Install]
WantedBy=multi-user.target

9
contrib/dailyjobs Normal file
View File

@ -0,0 +1,9 @@
# Run the daily, weekly, and monthly jobs if cronie-anacron is not installed
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# run-parts
02 4 * * * root [ ! -f /etc/cron.hourly/0anacron ] && run-parts /etc/cron.daily
22 4 * * 0 root [ ! -f /etc/cron.hourly/0anacron ] && run-parts /etc/cron.weekly
42 4 1 * * root [ ! -f /etc/cron.hourly/0anacron ] && run-parts /etc/cron.monthly

3
crond.sysconfig Normal file
View File

@ -0,0 +1,3 @@
# Settings for the CRON daemon.
# CRONDARGS= : any extra command-line startup arguments for crond
CRONDARGS=

132
cronie.init Executable file
View File

@ -0,0 +1,132 @@
#!/bin/sh
#
# crond Start/Stop the cron clock daemon.
#
# chkconfig: 2345 90 60
# description: cron is a standard UNIX program that runs user-specified \
# programs at periodic scheduled times. vixie cron adds a \
# number of features to the basic UNIX cron, including better \
# security and more powerful configuration options.
### BEGIN INIT INFO
# Provides: crond crontab
# Required-Start: $local_fs $syslog
# Required-Stop: $local_fs $syslog
# Default-Start: 2345
# Default-Stop: 90
# Short-Description: run cron daemon
# Description: cron is a standard UNIX program that runs user-specified
# programs at periodic scheduled times. vixie cron adds a
# number of features to the basic UNIX cron, including better
# security and more powerful configuration options.
### END INIT INFO
[ -f /etc/sysconfig/crond ] || {
[ "$1" = "status" ] && exit 4 || exit 6
}
RETVAL=0
prog="crond"
exec=/usr/sbin/crond
lockfile=/var/lock/subsys/crond
config=/etc/sysconfig/crond
# Source function library.
. /etc/rc.d/init.d/functions
[ $UID -eq 0 ] && [ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog
start() {
if [ $(id -ru) -ne 0 ] ; then
echo "User has insufficient privilege."
exit 4
fi
[ -x $exec ] || exit 5
[ -f $config ] || exit 6
printf "Starting $prog: "
daemon $prog $CRONDARGS
retval=$?
echo
[ $retval -eq 0 ] && touch $lockfile
}
stop() {
if [ $(id -ru) -ne 0 ] ; then
echo "User has insufficient privilege."
exit 4
fi
printf "Stopping $prog: "
if [ -n "`pidfileofproc $exec`" ]; then
killproc $exec
RETVAL=3
else
failure "Stopping $prog"
fi
retval=$?
echo
[ $retval -eq 0 ] && rm -f $lockfile
}
restart() {
rh_status_q && stop
start
}
reload() {
printf "Reloading $prog: "
if [ -n "`pidfileofproc $exec`" ]; then
killproc $exec -HUP
else
failure "Reloading $prog"
fi
retval=$?
echo
}
force_reload() {
# new configuration takes effect after restart
restart
}
rh_status() {
# run checks to determine if the service is running or use generic status
status -p /var/run/crond.pid $prog
}
rh_status_q() {
rh_status >/dev/null 2>&1
}
case "$1" in
start)
rh_status_q && exit 0
$1
;;
stop)
rh_status_q || exit 0
$1
;;
restart)
$1
;;
reload)
rh_status_q || exit 7
$1
;;
force-reload)
force_reload
;;
status)
rh_status
;;
condrestart|try-restart)
rh_status_q || exit 0
restart
;;
*)
echo "Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}"
exit 2
esac
exit $?

Binary file not shown.

View File

@ -1 +0,0 @@
a675051e62a715afb3efa87d50813f879c43955b

Binary file not shown.

View File

@ -1 +0,0 @@
021dcc67fe5ba149c31aa82085a05c37f080e7b9

Binary file not shown.

View File

@ -1 +0,0 @@
c3ce1e892ef195c372aacde1d4bff06d1007c657

137
cronie_common.c Normal file
View File

@ -0,0 +1,137 @@
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
/* Return:
* 0 - Not found
* 1 - Found */
static int find_envvar(const char *source, const char **start_pos, size_t *length) {
const char *reader;
size_t size = 1;
int waiting_close = 0;
*length = 0;
*start_pos = NULL;
if (source == NULL || *source == '\0') {
return 0;
}
*start_pos = strchr(source, '$');
if (*start_pos == NULL) {
return 0;
}
/* Skip $, since all envvars start with this char */
reader = *start_pos + 1;
while (*reader != '\0') {
if (*reader == '_' || isalnum(*reader)) {
if (size <= 2 && isdigit(*reader)) {
goto not_found;
}
size++;
}
else if (*reader == '{') {
if (size != 1) {
goto not_found;
}
size++;
waiting_close = 1;
}
else if (*reader == '}') {
if ((waiting_close && size == 2) || size == 1) {
goto not_found;
}
if (waiting_close) {
size++;
}
waiting_close = 0;
break;
}
else
break;
reader++;
}
if (waiting_close) {
goto not_found;
}
*length = size;
return 1;
not_found:
*length = 0;
*start_pos = NULL;
return 0;
}
/* Expand env variables in 'source' arg and save to 'result'
* Return:
* 1 - Success
* 0 - Fail */
int expand_envvar(const char *source, char *result, size_t max_size) {
const char *envvar_p;
size_t envvar_name_size = 0;
*result = '\0';
while (find_envvar(source, &envvar_p, &envvar_name_size)) {
char *envvar_name, *envvar_value;
size_t prefix_size;
/* Copy content before env var name */
prefix_size = envvar_p - source;
if (prefix_size > 0) {
if ((strlen(result) + prefix_size + 1) > max_size) {
goto too_big;
}
strncat(result, source, prefix_size);
}
/* skip envvar name */
source = envvar_p + envvar_name_size;
/* copy envvar name, ignoring $, { and } chars*/
envvar_p++;
envvar_name_size--;
if (*envvar_p == '{') {
envvar_p++;
envvar_name_size = envvar_name_size - 2;
}
envvar_name = malloc(envvar_name_size + 1);
strncpy(envvar_name, envvar_p, envvar_name_size);
envvar_name[envvar_name_size] = '\0';
/* Copy envvar value to result */
envvar_value = getenv(envvar_name);
free(envvar_name);
if (envvar_value != NULL) {
if ((strlen(result) + strlen(envvar_value) + 1) > max_size) {
goto too_big;
}
strcat(result, envvar_value);
}
}
/* Copy any character left in the source string */
if (*source != '\0') {
if ((strlen(result) + strlen(source) + 1) > max_size) {
goto too_big;
}
strcat(result, source);
}
return 1;
too_big:
return 0;
}

43
cronie_common.h Normal file
View File

@ -0,0 +1,43 @@
/*
* Copyright (c) 2012 Copyright Red Hat Software
*
* 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.
*/
/* Collection of definitions, inline functions, etc, that are useful for
* both cron and anacron. */
#ifndef CRONIE_COMMON_H
#define CRONIE_COMMON_H
#ifndef __attribute__
# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8)
# define __attribute__(x) /* empty */
# endif
#endif
#ifndef ATTRIBUTE_NORETURN
# define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__))
#endif
#ifndef ATTRIBUTE_UNUSED
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
#endif
#ifndef MAX_EMAILSTR
#define MAX_EMAILSTR 255 /* max length of email address strings (254 + \0) */
#endif
int expand_envvar(const char *, char *, size_t);
#endif /* CRONIE_COMMON_H */

16
debian/README.Debian vendored Normal file
View File

@ -0,0 +1,16 @@
cronie for Debian
-----------------
This version of cronie has been patched and configured to be as compatible as
possible to Debian's standard job scheduler, ISC cron. You should be able to
switch between these implementations without experiencing any major negative
side effects to your system.
Feature-wise, however, there still are quite a number of subtle and
not-so-subtle differences (mostly because of how much Debian's version of ISC
cron is patched), so be sure to read the manpages.
cronie installs a file /etc/cron.deny (empty), thereby enabling all users to
use the crontab(1) command by default.
-- Christian Kastner <ckk@debian.org> Sun, 20 Mar 2011 01:27:55 +0100

134
debian/changelog vendored Normal file
View File

@ -0,0 +1,134 @@
cronie (1.6.1-5) experimental; urgency=medium
* cronie pre-depends now on cron-daemon-common, and does no longer install
conflicting files.
-- Georges Khaznadar <georgesk@debian.org> Sun, 13 Nov 2022 17:09:31 +0100
cronie (1.6.1-4) experimental; urgency=medium
* fixed the watch file
-- Georges Khaznadar <georgesk@debian.org> Tue, 01 Nov 2022 21:53:41 +0100
cronie (1.6.1-3) experimental; urgency=medium
* reverted previous changes, as I did not notice the closed ITA bug
#974038, came back to contents authored by Lin Lance.
-- Georges Khaznadar <georgesk@debian.org> Thu, 12 May 2022 10:35:36 +0200
cronie (1.6.1-2) experimental; urgency=medium
* refreshed debian patches, which were targetted to version 1.5.5
-- Georges Khaznadar <georgesk@debian.org> Tue, 10 May 2022 17:56:24 +0200
cronie (1.6.1-1) experimental; urgency=medium
* New upstream version (1.6.1)
* Refreshed patches
* d/control: New maintainer (Closes: #974038)
* d/control: Updated standards (4.6.0), dh-compat (13)
-- Lance Lin <LQi254@protonmail.com> Tue, 03 May 2022 21:21:59 +0700
cronie (1.5.5-3) experimental; urgency=medium
* Add Hurd-workaround-for-PATH_MAX.patch (Closes: #638048)
* Fix spelling error in previous changelog entry
-- Christian Kastner <ckk@debian.org> Tue, 05 Nov 2019 08:04:37 +0100
cronie (1.5.5-2) experimental; urgency=medium
* Don't build /usr/sbin/anacron
/usr/sbin/anacron is still provided by src:anacron.
Thanks, Andreas Beckmann, for catching this! (Closes: #944024)
-- Christian Kastner <ckk@debian.org> Sun, 03 Nov 2019 11:12:33 +0100
cronie (1.5.5-1) experimental; urgency=medium
* New upstream version 1.5.5
* Drop patches (included upstream):
- crond-report-missing-newline-before-EOF.patch
- crontab-Add-Y-N-to-retry-prompt.patch
- crontab-fsync-to-check-for-full-disk.patch
- crontab.1-Various-fixes-and-improvements.patch
- entries-Explicitly-validate-upper-ranges-and-steps.patch
* Refresh patches
- debian/patches/Manpage-and-typo-fixes.patch
- debian/patches/Rename-PAM-service-to-cronie.patch
+ Split out Unbundle-upstream-PAM-config.patch from this one
-- Christian Kastner <ckk@debian.org> Thu, 31 Oct 2019 22:20:05 +0100
cronie (1.5.4-final-2) experimental; urgency=medium
* build: Set default EDITOR to /usr/bin/sensible-editor
* d/patches (added):
- crond-report-missing-newline-before-EOF.patch
- entries-Explicitly-validate-upper-ranges-and-steps.patch
- crontab.1-Various-fixes-and-improvements.patch
- crontab-Add-Y-N-to-retry-prompt.patch
- crontab-fsync-to-check-for-full-disk.patch
-- Christian Kastner <ckk@debian.org> Wed, 30 Oct 2019 21:12:01 +0100
cronie (1.5.4-final-1) experimental; urgency=medium
* New upstream release. (Closes: #697811, #783856)
[ Andreas Henriksson ]
* debian/watch: update for cronie move to github
* Modify patches to apply against new upstream release
* Add debian/gbp.conf
* Adjust and ship the cronie.service file
* Use debian/clean to remove src/cron-paths.h
* Fix lintian warning about not using default-mta
* Fix typo in patch tagging meta-header
[ Christian Kastner ]
* d/control:
- Switch Build-Depends from debhelper to debhelper-compat
- Bump debhelper compatibility level to 12
- Bump Standards-Version to 4.4.1 (no changes needed)
- Remove now obsolete d/compat file
- Set Rules-Requires-Root: no
We don't need (fake)root for building the package.
- Point Homepage to GitHub
- Set Vcs-* URLs for Salsa
- Mark package cronie as Multi-Arch: foreign
- Add Pre-Depends: ${misc:Pre-Depends} to binary package
As recommended by lintian's skip-systemd-native-flag-missing-pre-depends
* d/cronie.default: Add new daemon flag "-P"
* d/rules:
- Add hardening flags
- Stop passing actions to dh_installinit
This has been obsoleted by dependency-based booting in Wheezy, see
https://lists.debian.org/debian-devel/2013/05/msg01109.html
- Use DEB_HOST_ARCH_OS from /usr/share/dpkg/architecture.mk
- Proper passing of CFLAGS through DEB_CFLAGS_MAINT_APPEND
* d/copyright:
- Fix syntax errors
- Switch URL to official policy URL
- Point Source to GitHub
fedorahosted.org has been retired
- Bump copyrights
* d/clean: Remove generated files for (non-enabled) anacron build
* Sync maintscripts with src:cron
-- Christian Kastner <ckk@debian.org> Mon, 28 Oct 2019 19:35:38 +0100
cronie (1.4.8-1~exp1) experimental; urgency=low
* Initial release (Closes: #590876)
* debian/patches added:
- 0001-Unbundle-anacron
- 0002-Manpage-and-typo-fixes
- 0003-Rename-PAM-service-to-cronie
- 0004-Debian-specific-paths-and-features
- 0005-Extend-support-for-kFreeBSD-and-GNU-HURD
-- Christian Kastner <debian@kvr.at> Tue, 26 Jul 2011 14:00:34 +0200

2
debian/clean vendored Normal file
View File

@ -0,0 +1,2 @@
src/cron-paths.h
anacron/anacron-paths.h

46
debian/control vendored Normal file
View File

@ -0,0 +1,46 @@
Source: cronie
Section: admin
Priority: optional
Maintainer: Lance Lin <LQi254@protonmail.com>
Uploaders: Georges Khaznadar <georgesk@debian.org>
Build-Depends:
debhelper-compat (= 13),
libpam0g-dev,
libselinux1-dev [linux-any],
libaudit-dev [linux-any]
Rules-Requires-Root: no
Standards-Version: 4.6.0
Homepage: https://github.com/cronie-crond/cronie
Vcs-Git: https://salsa.debian.org/debian/cronie.git
Vcs-Browser: https://salsa.debian.org/debian/cronie
Package: cronie
Architecture: any
Multi-Arch: foreign
Pre-Depends:
${misc:Pre-Depends},
cron-daemon-common
Depends:
${shlibs:Depends},
${misc:Depends},
adduser,
lsb-base (>= 3.0-6),
libpam-runtime (>= 1.0.1-11),
sensible-utils
Recommends:
default-mta | mail-transport-agent
Suggests:
anacron (>= 2.0-1)
Provides: cron-daemon, cron
Conflicts: cron
Replaces: cron
Description: Process Scheduling Daemon
cronie is a daemon that runs specified programs at scheduled times and
optionally mails generated output to the user. It is a fork of the original
ISC cron and contains many improvements, such as:
* inotify support (Linux only)
* clustering support
* full PAM support
.
cronie is fully compatible with ISC cron (Debian's standard job scheduler),
and can be used as a drop-in replacement for it.

135
debian/copyright vendored Normal file
View File

@ -0,0 +1,135 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cronie
Upstream-Contact: Marcela Mašláňová <mmaslano@redhat.com>
Source: https://github.com/cronie-crond/cronie
Files: *
Copyright: 1988,1989,1990,1993,1994, The Regents of the University of California
1997,2000, Internet Software Consortium, Inc.
2004, Internet Systems Consortium, Inc. ("ISC")
1997-2019, Red Hat, Inc.
2000,2002, Todd C. Miller
2010, Colin Dean
License: ISC
Files: src/popen.c
Copyright: 1989,1993,1994,2005, The Regents of the University of California
License: BSD-2-clause
Files: src/bitstring.h
Copyright: 1989,1993,2003, The Regents of the University of California
License: BSD-3-clause
Files: anacron/*
Copyright: 1998, Itai Tzur <itzur@actcom.co.il>
1999, Sean 'Shaleh' Perry <shaleh@debian.org>
2004, Pascal Hakim <pasc@redellipse.net>
2009-2019, Red Hat, Inc.
License: GPL-2+
Files: debian/*
Copyright: 2010-2019, Christian Kastner <ckk@debian.org>
2018, Andreas Henriksson <andreas@fatal.se>
License: GPL-3+
Files: debian/patches/*
Copyright: 2019, Christian Kastner <ckk@debian.org>
License: ISC
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.
License: BSD-2-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
License: BSD-3-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the University nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
.
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
License: GPL-2+
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.
.
On Debian systems, the full text of the GNU General Public
License version 2 can be found in the file
"/usr/share/common-licenses/GPL-2".
License: GPL-3+
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 3 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, see <http://www.gnu.org/licenses/>.
.
On Debian systems, the full text of the GNU General Public
License version 3 can be found in the file
"/usr/share/common-licenses/GPL-3".

13
debian/cronie.default vendored Normal file
View File

@ -0,0 +1,13 @@
# Defaults for cronie initscript
# This is a POSIX shell fragment
# Some additional options for the daemon
# See cron(8) for all options and a detailed explanation of these
#
# -m <cmd> shell command to use for sending mail instead of sendmail(8)
# -p lift some restrictions on user crontabs (owner, mode, type)
# -P Don't set PATH; instead, inherit it from the environment
# -c enable clustering support
# -s send job output to syslog instead of mail
#
DAEMON_ARGS=""

6
debian/cronie.dirs vendored Normal file
View File

@ -0,0 +1,6 @@
etc/cron.d
etc/cron.hourly
etc/cron.daily
etc/cron.weekly
etc/cron.monthly
var/spool/cron/crontabs

87
debian/cronie.init vendored Normal file
View File

@ -0,0 +1,87 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: cronie
# Required-Start: $remote_fs $syslog $time
# Required-Stop: $remote_fs $syslog $time
# Default-Start: 2 3 4 5
# Default-Stop:
# Short-Description: time-based job scheduler
# Description: cronie is a daemon that runs specified programs at
# scheduled times and optionally mails generated output
# to the user.
### END INIT INFO
# Author: Christian Kastner <ckk@debian.org>
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="time-based job scheduler"
NAME=cronie
DAEMON_NAME=crond
DAEMON=/usr/sbin/$DAEMON_NAME
DAEMON_ARGS=""
PIDFILE=/var/run/$DAEMON_NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
# Exit if the package is not installed
[ -x $DAEMON ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
. /lib/lsb/init-functions
do_start()
{
start_daemon -p $PIDFILE $DAEMON $DAEMON_ARGS
}
do_stop()
{
killproc -p $PIDFILE $DAEMON
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC " "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 || exit 0 ;;
2|3) [ "$VERBOSE" != no ] && log_end_msg 1 || exit 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|3) [ "$VERBOSE" != no ] && log_end_msg 0 || exit 0 ;;
*) [ "$VERBOSE" != no ] && log_end_msg 1 || exit 1 ;;
esac
;;
status)
status_of_proc -p $PIDFILE "$DAEMON" "$DAEMON_NAME" && exit 0 || exit $?
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
if [ $? -eq 0 ]
then
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
else
# Failed to stop
log_end_msg 1
fi
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac

21
debian/cronie.pam vendored Normal file
View File

@ -0,0 +1,21 @@
# The PAM configuration file for cronie (cron daemon)
# Access control using /etc/security/access.conf
account required pam_access.so
# Set the loginuid process attribute
session required pam_loginuid.so
# Read environment variables from pam_env's default files, /etc/environment
# and /etc/security/pam_env.conf.
session required pam_env.so
# In addition to the above, read system locale information
session required pam_env.so envfile=/etc/default/locale
# Sets up user limits
session required pam_limits.so
@include common-account
@include common-session-noninteractive
@include common-auth

65
debian/cronie.postinst vendored Normal file
View File

@ -0,0 +1,65 @@
#!/bin/sh
set -e
# Analogous to Debian's ISC cron postinst script (for compatibility reasons)
crondir="/var/spool/cron"
action="$1"
if [ "$action" != configure ]
then
exit 0
fi
# Make sure group "crontab" exists (needed for running SGID)
getent group crontab > /dev/null 2>&1 || addgroup --system crontab
# Make crontab(1) SGID
if ! dpkg-statoverride --list /usr/bin/crontab > /dev/null
then
dpkg-statoverride --update --add root crontab 2755 /usr/bin/crontab
fi
# Adjust permissions for spool dir
# Can't use dpkg-statoverride for this because it doesn't cooperate nicely
# with cron alternatives such as bcron
if [ -d $crondir/crontabs ]
then
# This must be in sync with misc.c:check_spool_dir()
chown root:crontab $crondir/crontabs
chmod 1730 $crondir/crontabs
cd $crondir/crontabs
set +e
# Iterate over each entry in the spool directory, perform some sanity
# checks (see CVE-2017-9525), and chown/chgroup the crontabs
for tab_name in *
do
[ "$tab_name" = "*" ] && continue
tab_links=`stat -c '%h' "$tab_name"`
tab_owner=`stat -c '%U' "$tab_name"`
if [ ! -f "$tab_name" ]
then
echo "Warning: $tab_name is not a regular file!"
continue
elif [ "$tab_links" -ne 1 ]
then
echo "Warning: $tab_name has more than one hard link!"
continue
elif [ "$tab_owner" != "$tab_name" ]
then
echo "Warning: $tab_name name differs from owner $tab_owner!"
continue
fi
chown "$tab_owner:crontab" "$tab_name"
chmod 600 "$tab_name"
done
set -e
fi
#DEBHELPER#
exit 0

11
debian/cronie.postrm vendored Normal file
View File

@ -0,0 +1,11 @@
#!/bin/sh
set -e
if [ "$1" = "purge" ]
then
rm -f /etc/cron.allow /etc/cron.deny
fi
#DEBHELPER#
exit 0

6
debian/cronie.prerm vendored Normal file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -e
#DEBHELPER#
exit 0

2
debian/docs vendored Normal file
View File

@ -0,0 +1,2 @@
NEWS
README

0
debian/etc/cronie.deny vendored Normal file
View File

12
debian/etc/crontab.system vendored Normal file
View File

@ -0,0 +1,12 @@
# /etc/crontab: system-wide crontab
# See crontab(5)
# Set PATH to system default
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#

2
debian/etc/placeholder vendored Normal file
View File

@ -0,0 +1,2 @@
# DO NOT REMOVE
# this file prevents dpkg from removing this directory

10
debian/gbp.conf vendored Normal file
View File

@ -0,0 +1,10 @@
[DEFAULT]
pristine-tar = True
debian-branch = debian/master
upstream-branch = upstream/latest
[buildpackage]
sign-tags = True
[pq]
patch-numbers = False

View File

@ -0,0 +1,75 @@
From: Christian Kastner <ckk@kvr.at>
Date: Thu, 28 Jul 2011 11:15:01 +0200
Subject: Debian-specific paths and features
Use Debian-specific paths and features. For example, the spool dir differs from
upstream, and we always build to use syslog.
Forwarded: not-needed
Last-Update: 2011-07-28
---
man/cron.8 | 15 ++-------------
man/crontab.1 | 2 +-
man/crontab.5 | 2 +-
3 files changed, 4 insertions(+), 15 deletions(-)
--- a/man/cron.8
+++ b/man/cron.8
@@ -41,7 +41,7 @@
.PP
.I Cron
searches
-.I /var/spool/cron
+.I /var/spool/cron/crontabs
for crontab files which are named after user accounts;
together with the system crontab
.IR /etc/crontab ,
@@ -88,7 +88,7 @@
.IR /etc/cron.d/
directory that contains system cronjobs stored for different users.
.TP
-.IR /var/spool/cron
+.IR /var/spool/cron/crontabs
directory that contains user crontables created by the
.BR crontab (1)
command.
@@ -181,17 +181,6 @@
.TP
.B "\-V"
Print version and exit.
-.SH SIGNALS
-When the
-.I SIGHUP
-is received, the
-.I Cron
-daemon will close and reopen its log file. This proves to be useful in
-scripts which rotate and age log files. Naturally, this is not relevant
-if
-.I Cron
-was built to use
-.IR syslog (3).
.SH CLUSTERING SUPPORT
In this version of
.IR Cron
--- a/man/crontab.1
+++ b/man/crontab.1
@@ -68,7 +68,7 @@
In this version of
.IR Cron
it is possible to use a network-mounted shared
-.I /var/spool/cron
+.I /var/spool/cron/crontabs
across a cluster of hosts and specify that only one of the hosts should
run the crontab jobs in the particular directory at any one time. You
may also use
--- a/man/crontab.5
+++ b/man/crontab.5
@@ -315,7 +315,7 @@
.SH FILES
.I /etc/crontab
main system crontab file.
-.I /var/spool/cron/
+.I /var/spool/cron/crontabs
a directory for storing crontabs defined by users.
.I /etc/cron.d/
a directory for storing system crontabs.

View File

@ -0,0 +1,36 @@
From: Christian Kastner <ckk@kvr.at>
Date: Sun, 7 Aug 2011 19:48:09 +0200
Subject: Extend support for kFreeBSD and GNU HURD
Extend some of the #ifdefs to include kFreeBSD and HURD where it's obviously OK
to do so
Forwarded: no
Last-Update: 2011-08-07
---
src/entry.c | 2 +-
src/pathnames.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
--- a/src/entry.c
+++ b/src/entry.c
@@ -403,7 +403,7 @@
}
else
log_it("CRON", getpid(), "ERROR", "can't set LOGNAME", 0);
-#if defined(BSD) || defined(__linux)
+#if defined(BSD) || defined(__linux) || defined(__GLIBC__) || defined(__gnu_hurd__)
if (glue_strings(envstr, sizeof envstr, "USER", pw->pw_name, '=')) {
if ((tenvp = env_set(e->envp, envstr)) == NULL) {
ecode = e_memory;
--- a/src/pathnames.h
+++ b/src/pathnames.h
@@ -26,7 +26,7 @@
#ifndef _PATHNAMES_H_
#define _PATHNAMES_H_
-#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX)
+#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX) || defined(__GLIBC__) || defined(__gnu_hurd__)
# include <paths.h>
#endif /*BSD*/

View File

@ -0,0 +1,27 @@
From: Christian Kastner <ckk@kvr.at>
Date: Tue, 5 Nov 2019 07:52:09 +0100
Subject: Hurd workaround for PATH_MAX
PATH_MAX is not defined on GNU Hurd, which is legal according to POSIX.
https://www.gnu.org/software/hurd/hurd/porting/guidelines.html
Bug-Debian: https://bugs.debian.org/638048
---
src/macros.h | 5 +++++
1 file changed, 5 insertions(+)
--- a/src/macros.h
+++ b/src/macros.h
@@ -43,6 +43,11 @@
#define DEBUGGING FALSE
#endif
+/* Gnu Hurd has no limit on pathnames */
+#if !defined PATH_MAX && defined __GNU__
+#define PATH_MAX 4096
+#endif
+
#define INIT_PID 1 /* parent of orphans */
#define READ_PIPE 0 /* which end of a pipe pair do you read? */
#define WRITE_PIPE 1 /* or write to? */

View File

@ -0,0 +1,133 @@
From: Christian Kastner <ckk@kvr.at>
Date: Thu, 28 Jul 2011 11:07:40 +0200
Subject: Manpage and typo fixes
Correct some errors or clarify sections in the manpages; fix some typos
---
man/cron.8 | 43 ++++++++++++++++---------------------------
man/crontab.1 | 2 +-
man/crontab.5 | 2 +-
src/cron.c | 2 +-
4 files changed, 19 insertions(+), 30 deletions(-)
--- a/man/cron.8
+++ b/man/cron.8
@@ -37,23 +37,15 @@
.B -V
.SH DESCRIPTION
.I Cron
-is started from
-.I /etc/rc.d/init.d
-or
-.I /etc/init.d
-when classical sysvinit scripts are used. In case systemd is enabled, then unit file is installed into
-.I /lib/systemd/system/crond.service
-and daemon is started by
-.I systemctl start crond.service
-command. It returns immediately, thus, there is no need to need to start it with
-the '&' parameter.
+is automatically started at boot time.
.PP
.I Cron
searches
.I /var/spool/cron
-for crontab files which are named after accounts in
-.I /etc/passwd;
-The found crontabs are loaded into the memory.
+for crontab files which are named after user accounts;
+together with the system crontab
+.IR /etc/crontab ,
+the found crontabs are loaded into the memory.
.I Cron
also searches for
any files in the
@@ -71,12 +63,11 @@
option.
.PP
There are two ways how changes in crontables are checked. The first
-method is checking the modtime of a file. The second method is using the
-inotify support. Using of inotify is logged in the
-.I /var/log/cron
-log after the daemon is started. The inotify support checks for changes
-in all crontables and accesses the hard disk only when a change is
-detected.
+method is checking the modtime of a file. The second method
+is using inotify support, which is only available on Linux.
+When the daemon uses inotify, it logs this fact to syslog on startup.
+The inotify support checks for changes in all crontables and accesses the
+hard disk only when a change is detected.
.PP
When using the modtime option,
.I Cron
@@ -99,13 +90,8 @@
.TP
.IR /var/spool/cron
directory that contains user crontables created by the
-.IR crontab
-command.
-.PP
-Note that the
.BR crontab (1)
-command updates the modtime of the spool directory whenever it changes a
-crontab.
+command.
.PP
.SS Daylight Saving Time and other time changes
Local time changes of less than three hours, such as those caused by the
@@ -153,7 +139,6 @@
standard input and send it as a mail message to the recipients specified
in the mail headers. Specifying the string
.I "off"
-(i.e., crond -m off)
will disable the sending of mail.
.TP
.B "\-n"
@@ -167,10 +152,14 @@
.B "\-f"
the same as -n, consistent with other crond implementations.
.TP
+.B "\-i"
+Disables inotify support (if present)
+.TP
.B "\-p"
Allows
.I Cron
-to accept any user set crontables.
+to accept any user set crontables (read: lift owner, type and mode
+restrictions)
.TP
.B "\-P"
Don't set PATH. PATH is instead inherited from the environment.
--- a/man/crontab.1
+++ b/man/crontab.1
@@ -109,7 +109,7 @@
.IR /etc/cron.d/
directory.
.PP
-The temporary directory can be set in an environment variable. If it is
+The temporary directory can be set using the environment variable $TMPDIR. If it is
not set by the user, the
.I /tmp
directory is used.
--- a/man/crontab.5
+++ b/man/crontab.5
@@ -268,7 +268,7 @@
# run at 2:15pm on the first of every month -- output mailed to paul
15 14 1 * * $HOME/bin/monthly
# run at 10 pm on weekdays, annoy Joe
-0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?%
+0 22 * * 1-5 mail \-s "It's 10pm" joe%Joe,%%Where are your kids?%
23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday"
5 4 * * sun echo "run at 5 after 4 every sunday"
.fi
--- a/src/cron.c
+++ b/src/cron.c
@@ -179,7 +179,7 @@
fprintf(stderr, "\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " -h print this message \n");
- fprintf(stderr, " -i deamon runs without inotify support\n");
+ fprintf(stderr, " -i daemon runs without inotify support\n");
fprintf(stderr, " -m <comm> off, or specify preferred client for sending mails\n");
fprintf(stderr, " -n run in foreground\n");
fprintf(stderr, " -f run in foreground, the same as -n\n");

View File

@ -0,0 +1,24 @@
From: Christian Kastner <ckk@kvr.at>
Date: Thu, 28 Jul 2011 11:11:45 +0200
Subject: Rename PAM service to cronie
Upstream uses "crond"; we switch to "cron" to avoid confusion with Debian's ISC
cron (it uses "cron" but that might change).
Forwarded: not-needed
Last-Update: 2011-07-28
---
src/security.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/src/security.c
+++ b/src/security.c
@@ -195,7 +195,7 @@
int cron_start_pam(struct passwd *pw) {
int retcode = 0;
- retcode = pam_start("crond", pw->pw_name, &conv, &pamh);
+ retcode = pam_start("cronie", pw->pw_name, &conv, &pamh);
PAM_FAIL_CHECK;
retcode = pam_set_item(pamh, PAM_TTY, "cron");
PAM_FAIL_CHECK;

40
debian/patches/Unbundle-anacron.patch vendored Normal file
View File

@ -0,0 +1,40 @@
From: Christian Kastner <ckk@kvr.at>
Date: Thu, 28 Jul 2011 11:01:03 +0200
Subject: Unbundle anacron
Upstream has integrated anacron into cronie. Debian has its own package, so
we unbundle it (mostly by removing references to it).
Forwarded: not-needed
Last-Update: 2011-07-28
---
man/cron.8 | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
--- a/man/cron.8
+++ b/man/cron.8
@@ -56,8 +56,7 @@
The found crontabs are loaded into the memory.
.I Cron
also searches for
-.I /etc/anacrontab
-and any files in the
+any files in the
.I /etc/cron.d
directory, which have a different format (see
.BR crontab (5)).
@@ -91,12 +90,8 @@
checks these files and directories:
.TP
.IR /etc/crontab
-system crontab. Nowadays the file is empty by default. Originally it
-was usually used to run daily, weekly, monthly jobs. By default these
-jobs are now run through anacron which reads
-.IR /etc/anacrontab
-configuration file. See
-.BR anacrontab (5)
+system crontab, usually used to run daily, weekly, monthly jobs. See
+.BR crontab (5)
for more details.
.TP
.IR /etc/cron.d/

View File

@ -0,0 +1,27 @@
From: Christian Kastner <ckk@kvr.at>
Date: Thu, 31 Oct 2019 22:16:13 +0100
Subject: Unbundle upstream PAM config
We supply our own PAM config, tailored to the Debian setup.
Last-Update: 2019-10-31
---
Makefile.am | 7 -------
1 file changed, 7 deletions(-)
--- a/Makefile.am
+++ b/Makefile.am
@@ -24,13 +24,6 @@
contrib/cronie.systemd \
anacron/ChangeLog.anacron
-if PAM
-pamdir = $(sysconfdir)/pam.d
-dist_pam_DATA = pam/crond
-else
-EXTRA_DIST += pam/crond
-endif
-
include anacron/Makemodule.am
include man/Makemodule.am
include src/Makemodule.am

View File

@ -0,0 +1,27 @@
From: Andreas Henriksson <andreas@fatal.se>
Date: Mon, 28 Oct 2019 19:33:53 +0100
Subject: Adjust the cronie.service file for debian use
Use default file instead of sysconfig, as shipped by this
package (debian/cronie.default) and also modify the variable
on ExecStart line as for what is used in the shipped default
file.
Forwarded: not-needed
---
contrib/cronie.systemd | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
--- a/contrib/cronie.systemd
+++ b/contrib/cronie.systemd
@@ -3,8 +3,8 @@
After=auditd.service nss-user-lookup.target systemd-user-sessions.service time-sync.target ypbind.service autofs.service
[Service]
-EnvironmentFile=/etc/sysconfig/crond
-ExecStart=/usr/sbin/crond -n $CRONDARGS
+EnvironmentFile=/etc/default/cronie
+ExecStart=/usr/sbin/crond -n $DAEMON_ARGS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure

8
debian/patches/series vendored Normal file
View File

@ -0,0 +1,8 @@
Unbundle-anacron.patch
Manpage-and-typo-fixes.patch
Rename-PAM-service-to-cronie.patch
Unbundle-upstream-PAM-config.patch
Debian-specific-paths-and-features.patch
Extend-support-for-kFreeBSD-and-GNU-HURD.patch
cronie-service-debianization.patch
Hurd-workaround-for-PATH_MAX.patch

76
debian/rules vendored Executable file
View File

@ -0,0 +1,76 @@
#!/usr/bin/make -f
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
# For DEB_HOST_ARCH_OS
include /usr/share/dpkg/architecture.mk
# Add build flags
export DEB_CFLAGS_MAINT_APPEND = -Wall
# Add hardening flags
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
# Build the config options string
CONFIG_OPTIONS =
# anacron is still being provided by src:anacron
CONFIG_OPTIONS += --disable-anacron
# Default EDITOR path
CONFIG_OPTIONS += --with-editor=/usr/bin/sensible-editor
# PAM is enabled by default
ifeq (,$(findstring nopam,$(DEB_BUILD_OPTIONS)))
CONFIG_OPTIONS += --with-pam
endif
##########################
### Linux-only options ###
ifeq ($(DEB_HOST_ARCH_OS), linux)
# SELINUX is enabled by default
ifeq (,$(findstring noselinux,$(DEB_BUILD_OPTIONS)))
CONFIG_OPTIONS += --with-selinux
endif
# inotify is enabled by default
ifeq (,$(findstring noinotify,$(DEB_BUILD_OPTIONS)))
CONFIG_OPTIONS += --with-inotify
endif
# audit is disabled by default
ifneq (,$(findstring withaudit,$(DEB_BUILD_OPTIONS)))
CONFIG_OPTIONS += --with-audit
endif
endif
##### End Linux-only #####
##########################
%:
dh $@
# Set SPOOL_DIR to Debian's traditional location for crontabs (see Policy)
# Set CRON_GROUP to "crontab" to enable SGID functionality (avoids SUID)
override_dh_auto_configure:
SPOOL_DIR=/var/spool/cron/crontabs \
dh_auto_configure CRON_GROUP=crontab -- $(CONFIG_OPTIONS)
override_dh_install:
dh_install
# Create /etc/cron.deny to allow crontab(1) for all users by default
install -m 644 debian/etc/cronie.deny debian/cronie/etc/cron.deny
# System-wide crontab: IS NOT INSTALLED HERE: SEE CON-DAEMON-COMMON
#install -m 644 debian/etc/crontab.system debian/cronie/etc/crontab
# Placeholders for dpkg: ARE NOT INSTALLED HERE: SEE CON-DAEMON-COMMON
#install -m 644 debian/etc/placeholder debian/cronie/etc/cron.d/.placeholder
#install -m 644 debian/etc/placeholder debian/cronie/etc/cron.hourly/.placeholder
#install -m 644 debian/etc/placeholder debian/cronie/etc/cron.daily/.placeholder
#install -m 644 debian/etc/placeholder debian/cronie/etc/cron.weekly/.placeholder
#install -m 644 debian/etc/placeholder debian/cronie/etc/cron.monthly/.placeholder
# systemd service file
install -d -m 755 debian/cronie/lib/systemd/system/
install -m 644 contrib/cronie.systemd debian/cronie/lib/systemd/system/cronie.service

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (quilt)

8
debian/watch vendored Normal file
View File

@ -0,0 +1,8 @@
version=4
# using Github's API with tags
opts="searchmode=plain,\
filenamemangle=s%v?@ANY_VERSION@%@PACKAGE@-$1.tar.gz%" \
https://api.github.com/repos/cronie-crond/cronie/tags?per_page=100 \
https://api.github.com/repos/[^/]+/[^/]+/tarball/refs/tags/v?@ANY_VERSION@

791
depcomp Executable file
View File

@ -0,0 +1,791 @@
#! /bin/sh
# depcomp - compile a program generating dependencies as side-effects
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1999-2018 Free Software Foundation, Inc.
# 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, 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, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
Run PROGRAMS ARGS to compile a file, generating dependencies
as side-effects.
Environment variables:
depmode Dependency tracking mode.
source Source file read by 'PROGRAMS ARGS'.
object Object file output by 'PROGRAMS ARGS'.
DEPDIR directory where to store dependencies.
depfile Dependency file to output.
tmpdepfile Temporary file to use when outputting dependencies.
libtool Whether libtool is used (yes/no).
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "depcomp $scriptversion"
exit $?
;;
esac
# Get the directory component of the given path, and save it in the
# global variables '$dir'. Note that this directory component will
# be either empty or ending with a '/' character. This is deliberate.
set_dir_from ()
{
case $1 in
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
*) dir=;;
esac
}
# Get the suffix-stripped basename of the given path, and save it the
# global variable '$base'.
set_base_from ()
{
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
}
# If no dependency file was actually created by the compiler invocation,
# we still have to create a dummy depfile, to avoid errors with the
# Makefile "include basename.Plo" scheme.
make_dummy_depfile ()
{
echo "#dummy" > "$depfile"
}
# Factor out some common post-processing of the generated depfile.
# Requires the auxiliary global variable '$tmpdepfile' to be set.
aix_post_process_depfile ()
{
# If the compiler actually managed to produce a dependency file,
# post-process it.
if test -f "$tmpdepfile"; then
# Each line is of the form 'foo.o: dependency.h'.
# Do two passes, one to just change these to
# $object: dependency.h
# and one to simply output
# dependency.h:
# which is needed to avoid the deleted-header problem.
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
} > "$depfile"
rm -f "$tmpdepfile"
else
make_dummy_depfile
fi
}
# A tabulation character.
tab=' '
# A newline character.
nl='
'
# Character ranges might be problematic outside the C locale.
# These definitions help.
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
lower=abcdefghijklmnopqrstuvwxyz
digits=0123456789
alpha=${upper}${lower}
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
echo "depcomp: Variables source, object and depmode must be set" 1>&2
exit 1
fi
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
depfile=${depfile-`echo "$object" |
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
rm -f "$tmpdepfile"
# Avoid interferences from the environment.
gccflag= dashmflag=
# Some modes work just like other modes, but use different flags. We
# parameterize here, but still list the modes in the big case below,
# to make depend.m4 easier to write. Note that we *cannot* use a case
# here, because this file can only contain one case statement.
if test "$depmode" = hp; then
# HP compiler uses -M and no extra arg.
gccflag=-M
depmode=gcc
fi
if test "$depmode" = dashXmstdout; then
# This is just like dashmstdout with a different argument.
dashmflag=-xM
depmode=dashmstdout
fi
cygpath_u="cygpath -u -f -"
if test "$depmode" = msvcmsys; then
# This is just like msvisualcpp but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvisualcpp
fi
if test "$depmode" = msvc7msys; then
# This is just like msvc7 but w/o cygpath translation.
# Just convert the backslash-escaped backslashes to single forward
# slashes to satisfy depend.m4
cygpath_u='sed s,\\\\,/,g'
depmode=msvc7
fi
if test "$depmode" = xlc; then
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
gccflag=-qmakedep=gcc,-MF
depmode=gcc
fi
case "$depmode" in
gcc3)
## gcc 3 implements dependency tracking that does exactly what
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
## it if -MD -MP comes after the -MF stuff. Hmm.
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
## the command line argument order; so add the flags where they
## appear in depend2.am. Note that the slowdown incurred here
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
for arg
do
case $arg in
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
*) set fnord "$@" "$arg" ;;
esac
shift # fnord
shift # $arg
done
"$@"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
mv "$tmpdepfile" "$depfile"
;;
gcc)
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
## (see the conditional assignment to $gccflag above).
## There are various ways to get dependency output from gcc. Here's
## why we pick this rather obscure method:
## - Don't want to use -MD because we'd like the dependencies to end
## up in a subdir. Having to rename by hand is ugly.
## (We might end up doing this anyway to support other compilers.)
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
## -MM, not -M (despite what the docs say). Also, it might not be
## supported by the other compilers which use the 'gcc' depmode.
## - Using -M directly means running the compiler twice (even worse
## than renaming).
if test -z "$gccflag"; then
gccflag=-MD,
fi
"$@" -Wp,"$gccflag$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The second -e expression handles DOS-style file names with drive
# letters.
sed -e 's/^[^:]*: / /' \
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
## This next piece of magic avoids the "deleted header file" problem.
## The problem is that when a header file which appears in a .P file
## is deleted, the dependency causes make to die (because there is
## typically no way to rebuild the header). We avoid this by adding
## dummy dependencies for each header file. Too bad gcc doesn't do
## this for us directly.
## Some versions of gcc put a space before the ':'. On the theory
## that the space means something, we add a space to the output as
## well. hp depmode also adds that space, but also prefixes the VPATH
## to the object. Take care to not repeat it in the output.
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
sgi)
if test "$libtool" = yes; then
"$@" "-Wp,-MDupdate,$tmpdepfile"
else
"$@" -MDupdate "$tmpdepfile"
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
echo "$object : \\" > "$depfile"
# Clip off the initial element (the dependent). Don't try to be
# clever and replace this with sed code, as IRIX sed won't handle
# lines with more than a fixed number of characters (4096 in
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
# the IRIX cc adds comments like '#:fec' to the end of the
# dependency line.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
| tr "$nl" ' ' >> "$depfile"
echo >> "$depfile"
# The second pass generates a dummy entry for each header file.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
>> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile"
;;
xlc)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
aix)
# The C for AIX Compiler uses -M and outputs the dependencies
# in a .u file. In older versions, this file always lives in the
# current directory. Also, the AIX compiler puts '$object:' at the
# start of each line; $object doesn't have directory information.
# Version 6 uses the directory in both cases.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.u
tmpdepfile2=$base.u
tmpdepfile3=$dir.libs/$base.u
"$@" -Wc,-M
else
tmpdepfile1=$dir$base.u
tmpdepfile2=$dir$base.u
tmpdepfile3=$dir$base.u
"$@" -M
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
aix_post_process_depfile
;;
tcc)
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
# FIXME: That version still under development at the moment of writing.
# Make that this statement remains true also for stable, released
# versions.
# It will wrap lines (doesn't matter whether long or short) with a
# trailing '\', as in:
#
# foo.o : \
# foo.c \
# foo.h \
#
# It will put a trailing '\' even on the last line, and will use leading
# spaces rather than leading tabs (at least since its commit 0394caf7
# "Emit spaces for -MD").
"$@" -MD -MF "$tmpdepfile"
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
# We have to change lines of the first kind to '$object: \'.
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
# And for each line of the second kind, we have to emit a 'dep.h:'
# dummy dependency, to avoid the deleted-header problem.
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
rm -f "$tmpdepfile"
;;
## The order of this option in the case statement is important, since the
## shell code in configure will try each of these formats in the order
## listed in this file. A plain '-MD' option would be understood by many
## compilers, so we must ensure this comes after the gcc and icc options.
pgcc)
# Portland's C compiler understands '-MD'.
# Will always output deps to 'file.d' where file is the root name of the
# source file under compilation, even if file resides in a subdirectory.
# The object file name does not affect the name of the '.d' file.
# pgcc 10.2 will output
# foo.o: sub/foo.c sub/foo.h
# and will wrap long lines using '\' :
# foo.o: sub/foo.c ... \
# sub/foo.h ... \
# ...
set_dir_from "$object"
# Use the source, not the object, to determine the base name, since
# that's sadly what pgcc will do too.
set_base_from "$source"
tmpdepfile=$base.d
# For projects that build the same source file twice into different object
# files, the pgcc approach of using the *source* file root name can cause
# problems in parallel builds. Use a locking strategy to avoid stomping on
# the same $tmpdepfile.
lockdir=$base.d-lock
trap "
echo '$0: caught signal, cleaning up...' >&2
rmdir '$lockdir'
exit 1
" 1 2 13 15
numtries=100
i=$numtries
while test $i -gt 0; do
# mkdir is a portable test-and-set.
if mkdir "$lockdir" 2>/dev/null; then
# This process acquired the lock.
"$@" -MD
stat=$?
# Release the lock.
rmdir "$lockdir"
break
else
# If the lock is being held by a different process, wait
# until the winning process is done or we timeout.
while test -d "$lockdir" && test $i -gt 0; do
sleep 1
i=`expr $i - 1`
done
fi
i=`expr $i - 1`
done
trap - 1 2 13 15
if test $i -le 0; then
echo "$0: failed to acquire lock after $numtries attempts" >&2
echo "$0: check lockdir '$lockdir'" >&2
exit 1
fi
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each line is of the form `foo.o: dependent.h',
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
# Do two passes, one to just change these to
# `$object: dependent.h' and one to simply `dependent.h:'.
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp2)
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
# compilers, which have integrated preprocessors. The correct option
# to use with these is +Maked; it writes dependencies to a file named
# 'foo.d', which lands next to the object file, wherever that
# happens to be.
# Much of this is similar to the tru64 case; see comments there.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir.libs/$base.d
"$@" -Wc,+Maked
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
"$@" +Maked
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
do
test -f "$tmpdepfile" && break
done
if test -f "$tmpdepfile"; then
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
# Add 'dependent.h:' lines.
sed -ne '2,${
s/^ *//
s/ \\*$//
s/$/:/
p
}' "$tmpdepfile" >> "$depfile"
else
make_dummy_depfile
fi
rm -f "$tmpdepfile" "$tmpdepfile2"
;;
tru64)
# The Tru64 compiler uses -MD to generate dependencies as a side
# effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'.
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
# dependencies in 'foo.d' instead, so we check for that too.
# Subdirectories are respected.
set_dir_from "$object"
set_base_from "$object"
if test "$libtool" = yes; then
# Libtool generates 2 separate objects for the 2 libraries. These
# two compilations output dependencies in $dir.libs/$base.o.d and
# in $dir$base.o.d. We have to check for both files, because
# one of the two compilations can be disabled. We should prefer
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
# automatically cleaned when .libs/ is deleted, while ignoring
# the former would cause a distcleancheck panic.
tmpdepfile1=$dir$base.o.d # libtool 1.5
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
"$@" -Wc,-MD
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
tmpdepfile3=$dir$base.d
"$@" -MD
fi
stat=$?
if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
do
test -f "$tmpdepfile" && break
done
# Same post-processing that is required for AIX mode.
aix_post_process_depfile
;;
msvc7)
if test "$libtool" = yes; then
showIncludes=-Wc,-showIncludes
else
showIncludes=-showIncludes
fi
"$@" $showIncludes > "$tmpdepfile"
stat=$?
grep -v '^Note: including file: ' "$tmpdepfile"
if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
# The first sed program below extracts the file names and escapes
# backslashes for cygpath. The second sed program outputs the file
# name when reading, but also accumulates all include files in the
# hold buffer in order to output them again at the end. This only
# works with sed implementations that can handle large buffers.
sed < "$tmpdepfile" -n '
/^Note: including file: *\(.*\)/ {
s//\1/
s/\\/\\\\/g
p
}' | $cygpath_u | sort -u | sed -n '
s/ /\\ /g
s/\(.*\)/'"$tab"'\1 \\/p
s/.\(.*\) \\/\1:/
H
$ {
s/.*/'"$tab"'/
G
p
}' >> "$depfile"
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
rm -f "$tmpdepfile"
;;
msvc7msys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
#nosideeffect)
# This comment above is used by automake to tell side-effect
# dependency tracking mechanisms from slower ones.
dashmstdout)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout, regardless of -o.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
test -z "$dashmflag" && dashmflag=-M
# Require at least two characters before searching for ':'
# in the target name. This is to cope with DOS-style filenames:
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
"$@" $dashmflag |
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this sed invocation
# correctly. Breaking it into two sed invocations is a workaround.
tr ' ' "$nl" < "$tmpdepfile" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
dashXmstdout)
# This case only exists to satisfy depend.m4. It is never actually
# run, as this mode is specially recognized in the preamble.
exit 1
;;
makedepend)
"$@" || exit $?
# Remove any Libtool call
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# X makedepend
shift
cleared=no eat=no
for arg
do
case $cleared in
no)
set ""; shift
cleared=yes ;;
esac
if test $eat = yes; then
eat=no
continue
fi
case "$arg" in
-D*|-I*)
set fnord "$@" "$arg"; shift ;;
# Strip any option that makedepend may not understand. Remove
# the object too, otherwise makedepend will parse it as a source file.
-arch)
eat=yes ;;
-*|$object)
;;
*)
set fnord "$@" "$arg"; shift ;;
esac
done
obj_suffix=`echo "$object" | sed 's/^.*\././'`
touch "$tmpdepfile"
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
rm -f "$depfile"
# makedepend may prepend the VPATH from the source file name to the object.
# No need to regex-escape $object, excess matching of '.' is harmless.
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process the last invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed '1,2d' "$tmpdepfile" \
| tr ' ' "$nl" \
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
| sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile" "$tmpdepfile".bak
;;
cpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
# Remove '-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
"$@" -E \
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
| sed '$ s: \\$::' > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
cat < "$tmpdepfile" >> "$depfile"
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvisualcpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test "X$1" != 'X--mode=compile'; do
shift
done
shift
fi
IFS=" "
for arg
do
case "$arg" in
-o)
shift
;;
$object)
shift
;;
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
set fnord "$@"
shift
shift
;;
*)
set fnord "$@" "$arg"
shift
shift
;;
esac
done
"$@" -E 2>/dev/null |
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
echo "$tab" >> "$depfile"
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvcmsys)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
none)
exec "$@"
;;
*)
echo "Unknown depmode $depmode" 1>&2
exit 1
;;
esac
exit 0
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

518
install-sh Executable file
View File

@ -0,0 +1,518 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2018-03-11.20; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# 'make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
tab=' '
nl='
'
IFS=" $tab$nl"
# Set DOITPROG to "echo" to test this script.
doit=${DOITPROG-}
doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
chgrpprog=${CHGRPPROG-chgrp}
chmodprog=${CHMODPROG-chmod}
chownprog=${CHOWNPROG-chown}
cmpprog=${CMPPROG-cmp}
cpprog=${CPPROG-cp}
mkdirprog=${MKDIRPROG-mkdir}
mvprog=${MVPROG-mv}
rmprog=${RMPROG-rm}
stripprog=${STRIPPROG-strip}
posix_mkdir=
# Desired mode of installed file.
mode=0755
chgrpcmd=
chmodcmd=$chmodprog
chowncmd=
mvcmd=$mvprog
rmcmd="$rmprog -f"
stripcmd=
src=
dst=
dir_arg=
dst_arg=
copy_on_change=false
is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
--help display this help and exit.
--version display version info and exit.
-c (ignored)
-C install only if different (preserve the last data modification time)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-s $stripprog installed files.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
RMPROG STRIPPROG
"
while test $# -ne 0; do
case $1 in
-c) ;;
-C) copy_on_change=true;;
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
case $mode in
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
shift;;
-o) chowncmd="$chownprog $2"
shift;;
-s) stripcmd=$stripprog;;
-t)
is_target_a_directory=always
dst_arg=$2
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
shift;;
-T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
shift
done
# We allow the use of options -d and -T together, by making -d
# take the precedence; this is for compatibility with GNU install.
if test -n "$dir_arg"; then
if test -n "$dst_arg"; then
echo "$0: target directory not allowed when installing a directory." >&2
exit 1
fi
fi
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dst_arg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dst_arg"
shift # fnord
fi
shift # arg
dst_arg=$arg
# Protect names problematic for 'test' and other utilities.
case $dst_arg in
-* | [=\(\)!]) dst_arg=./$dst_arg;;
esac
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call 'install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
if test $# -gt 1 || test "$is_target_a_directory" = always; then
if test ! -d "$dst_arg"; then
echo "$0: $dst_arg: Is not a directory." >&2
exit 1
fi
fi
fi
if test -z "$dir_arg"; then
do_exit='(exit $ret); exit $ret'
trap "ret=129; $do_exit" 1
trap "ret=130; $do_exit" 2
trap "ret=141; $do_exit" 13
trap "ret=143; $do_exit" 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names problematic for 'test' and other utilities.
case $src in
-* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dst_arg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dst_arg
# If destination is a directory, append the input filename.
if test -d "$dst"; then
if test "$is_target_a_directory" = never; then
echo "$0: $dst_arg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dstbase=`basename "$src"`
case $dst in
*/) dst=$dst$dstbase;;
*) dst=$dst/$dstbase;;
esac
dstdir_status=0
else
dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
fi
case $dstdir in
*/) dstdirslash=$dstdir;;
*) dstdirslash=$dstdir/;;
esac
obsolete_mkdir_used=false
if test $dstdir_status != 0; then
case $posix_mkdir in
'')
# Create intermediate dirs using mode 755 as modified by the umask.
# This is like FreeBSD 'install' as of 1997-10-28.
umask=`umask`
case $stripcmd.$umask in
# Optimize common cases.
*[2367][2367]) mkdir_umask=$umask;;
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
*[0-7])
mkdir_umask=`expr $umask + 22 \
- $umask % 100 % 40 + $umask % 20 \
- $umask % 10 % 4 + $umask % 2
`;;
*) mkdir_umask=$umask,go-w;;
esac
# With -d, create the new directory with the user-specified mode.
# Otherwise, rely on $mkdir_umask.
if test -n "$dir_arg"; then
mkdir_mode=-m$mode
else
mkdir_mode=
fi
posix_mkdir=false
case $umask in
*[123567][0-7][0-7])
# POSIX mkdir -p sets u+wx bits regardless of umask, which
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
;;
*)
# Note that $RANDOM variable is not portable (e.g. dash); Use it
# here however when possible just to lower collision chance.
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0
# Because "mkdir -p" follows existing symlinks and we likely work
# directly in world-writeable /tmp, make sure that the '$tmpdir'
# directory is successfully created first before we actually test
# 'mkdir -p' feature.
if (umask $mkdir_umask &&
$mkdirprog $mkdir_mode "$tmpdir" &&
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
then
if test -z "$dir_arg" || {
# Check for POSIX incompatibilities with -m.
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
# other-writable bit of parent directory when it shouldn't.
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
test_tmpdir="$tmpdir/a"
ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
case $ls_ld_tmpdir in
d????-?r-*) different_mode=700;;
d????-?--*) different_mode=755;;
*) false;;
esac &&
$mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
}
}
then posix_mkdir=:
fi
rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
else
# Remove any dirs left behind by ancient mkdir implementations.
rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
fi
trap '' 0;;
esac;;
esac
if
$posix_mkdir && (
umask $mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
# The umask is ridiculous, or mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix='/';;
[-=\(\)!]*) prefix='./';;
*) prefix='';;
esac
oIFS=$IFS
IFS=/
set -f
set fnord $dstdir
shift
set +f
IFS=$oIFS
prefixes=
for d
do
test X"$d" = X && continue
prefix=$prefix$d
if test -d "$prefix"; then
prefixes=
else
if $posix_mkdir; then
(umask=$mkdir_umask &&
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
# Don't fail if two instances are running concurrently.
test -d "$prefix" || exit 1
else
case $prefix in
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
*) qprefix=$prefix;;
esac
prefixes="$prefixes '$qprefix'"
fi
fi
prefix=$prefix/
done
if test -n "$prefixes"; then
# Don't fail if two instances are running concurrently.
(umask $mkdir_umask &&
eval "\$doit_exec \$mkdirprog $prefixes") ||
test -d "$dstdir" || exit 1
obsolete_mkdir_used=true
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=${dstdirslash}_inst.$$_
rmtmp=${dstdirslash}_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# If -C, don't bother to copy if it wouldn't change the file.
if $copy_on_change &&
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
rm -f "$dsttmp"
else
# Rename the file to the real destination.
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
# The rename failed, perhaps because mv can't rename something else
# to itself, or perhaps because mv is so ancient that it does not
# support -f.
{
# Now remove or move aside any old file at destination location.
# We try this two ways since rm can't unlink itself on some
# systems and the destination file might be busy for other
# reasons. In this case, the final cleanup might fail but the new
# file should still install successfully.
{
test ! -f "$dst" ||
$doit $rmcmd -f "$dst" 2>/dev/null ||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
} ||
{ echo "$0: cannot unlink or rename $dst" >&2
(exit 1); exit 1
}
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
fi || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

16
man/Makemodule.am Normal file
View File

@ -0,0 +1,16 @@
dist_man_MANS = \
man/cron.8 \
man/crond.8 \
man/cronnext.1 \
man/crontab.1 \
man/crontab.5
anacron_man = \
man/anacrontab.5 \
man/anacron.8
EXTRA_DIST += $(anacron_man)
if ANACRON
dist_man_MANS += $(anacron_man)
endif

217
man/anacron.8 Normal file
View File

@ -0,0 +1,217 @@
.TH ANACRON 8 2012-11-22 "cronie" "System Administration"
.SH NAME
anacron \- runs commands periodically
.SH SYNOPSIS
.B anacron \fR[\fB-s\fR] [\fB-f\fR] [\fB-n\fR] [\fB-d\fR] [\fB-q\fR]
[\fB-t anacrontab\fR] [\fB-S spooldir\fR] [\fIjob\fR]
.br
.B anacron \fR[\fB-S spooldir\fR] -u [\fB-t anacrontab\fR] \fR[\fIjob\fR]
.br
.B anacron \fR[\fB-V\fR|\fB-h\fR]
.br
.B anacron -T \fR[\fB-t anacrontab\fR]
.SH DESCRIPTION
.B Anacron
is used to execute commands periodically, with a frequency specified in
days. Unlike
.BR cron(8) ,
it does not assume that the machine is running continuously. Hence, it
can be used on machines that are not running 24 hours a day to control
regular jobs as daily, weekly, and monthly jobs.
.PP
Anacron reads a list of jobs from the
.I /etc/anacrontab
configuration file (see
.BR anacrontab (5)).
This file contains the list of jobs that Anacron controls. Each job
entry specifies a period in days, a delay in minutes, a unique job
identifier, and a shell command.
.PP
For each job, Anacron checks whether this job has been executed in the
last
.B n
days, where
.B n
is the time period specified for that job. If a job has not been
executed in
.B n
days or more, Anacron runs the job's shell command, after waiting for the
number of minutes specified as the delay parameter.
.PP
After the command exits, Anacron records the date (excludes the hour) in
a special timestamp file for that job, so it knows when to execute that
job again.
.PP
When there are no more jobs to be run, Anacron exits.
.PP
Anacron only considers jobs whose identifier, as specified in
.BR anacrontab (5),
matches any of the
.I job
command-line arguments. The
.I job
command-line arguments can be represented by shell wildcard patterns (be
sure to protect them from your shell with adequate quoting). Specifying
no
.I job
command-line arguments is equivalent to specifying "*" (that is, all
jobs are considered by Anacron).
.PP
Unless Anacron is run with the
.B \-d
option (specified below), it forks to the background when it starts, and
any parent processes exit immediately.
.PP
Unless Anacron is run with the
.B \-s
or
.B \-n
options, it starts jobs immediately when their delay is over. The
execution of different jobs is completely independent.
.PP
If an executed job generates any output to standard output or to standard
error, the output is mailed to the user under whom Anacron is running
(usually root), or to the address specified in the
.B MAILTO
environment variable in the
.I /etc/anacrontab
file, if such exists. If the
.B LOGNAME
environment variable is set, it is used in the From: field of the mail.
.PP
Any informative messages generated by Anacron are sent to
.BR syslogd (8)
or
.BR rsyslogd (8)
under with facility set to
.B cron
and priority set to
.BR notice .
Any error messages are sent with the priority
.BR error .
.PP
"Active" jobs (i.e., jobs that Anacron already decided to run and are now
waiting for their delay to pass, and jobs that are currently being
executed by Anacron), are "locked", so that other copies of Anacron
cannot run them at the same time.
.SH OPTIONS
.TP
.B \-f
Forces execution of all jobs, ignoring any timestamps.
.TP
.B \-u
Updates the timestamps of all jobs to the current date, but does not run
any.
.TP
.B \-s
Serializes execution of jobs. Anacron does not start a new job before the
previous one finished.
.TP
.B \-n
Runs jobs immediately and ignores the specified delays in the
.I /etc/anacrontab
file. This options implies
.BR -s .
.TP
.B \-d
Does not fork Anacron to the background. In this mode, Anacron will
output informational messages to standard error, as well as to syslog.
The output of any job is mailed by Anacron.
.TP
.B \-q
Suppresses any messages to standard error. Only applicable with
.BR -d .
.TP
.B -t some_anacrontab
Uses the specified anacrontab, rather than the
.I /etc/anacrontab
default one.
.TP
.B -T
Anacrontab testing. Tests the
.I /etc/anacrontab
configuration file for validity. If there is an error in the file, it is
shown on the standard output and Anacron returns the value of 1. Valid
anacrontabs return the value of 0.
.TP
.B -S spooldir
Uses the specified spooldir to store timestamps in. This option is
required for users who wish to run anacron themselves.
.TP
.B -V
Prints version information, and exits.
.TP
.B -h
Prints short usage message, and exits.
.SH SIGNALS
After receiving a
.B SIGUSR1
signal, Anacron waits for any running jobs to finish and then exits.
This can be used to stop Anacron cleanly.
.SH NOTES
Make sure your time-zone is set correctly before Anacron is started since
the time-zone affects the date. This is usually accomplished by setting
the TZ environment variable, or by installing a
.I /usr/lib/zoneinfo/localtime
file. See
.BR tzset (3)
for more information.
.PP
Timestamp files are created in the spool directory for each job specified
in an anacrontab. These files are never removed automatically by
Anacron, and should be removed by hand if a job is no longer being
scheduled.
.SH FILES
.TP
.I /etc/anacrontab
Contains specifications of jobs. See
.BR anacrontab (5)
for a complete description.
.TP
.I /var/spool/anacron
This directory is used by Anacron for storing timestamp files.
.SH "SEE ALSO"
.BR anacrontab (5),
.BR cron (8),
.BR tzset (3)
.PP
The Anacron
.I README
file.
.SH BUGS
Anacron never removes timestamp files. Remove unused files manually.
.PP
Anacron uses up to two file descriptors for each active job. It may run
out of descriptors if there are lots of active jobs. See
.B echo $(($(ulimit -n) / 2))
for information how many concurent jobs anacron may run.
.PP
Mail comments, suggestions and bug reports to
.MT shaleh@\:(debian.\:org|\:valinux.\:com)
Sean 'Shaleh' Perry
.ME .
.SH AUTHOR
Anacron was originally conceived and implemented by
.MT schwarz@\:monet.\:m.\:isar.\:de
Christian Schwarz
.ME .
.PP
The current implementation is a complete rewrite by
.MT itzur@\:actcom.\:co.\:il
Itai Tzur
.ME .
.PP
The code base was maintained by
.MT shaleh@\:(debian.\:org|\:valinux.\:com)
Sean 'Shaleh' Perry
.ME .
.PP
Since 2004, it is maintained by
.MT pasc@\:(debian.\:org|\:redellipse.\:net)
Pascal Hakim
.ME .
.PP
For Fedora, Anacron is maintained by
.MT mmaslano@redhat.\:com
Marcela Mašláňová
.ME .

141
man/anacrontab.5 Normal file
View File

@ -0,0 +1,141 @@
.TH ANACRONTAB 5 2012-11-22 "cronie" "File Formats"
.SH NAME
/etc/anacrontab \- configuration file for Anacron
.SH DESCRIPTION
The
.I /etc/anacrontab
configuration file describes the jobs controlled by
.BR anacron (8).
It can contain three types of lines: job-description lines, environment
assignments, or empty lines.
.PP
Job-description lines can have the following format:
.PP
period in days delay in minutes job-identifier command
.PP
The
.I period in days
variable specifies the frequency of execution of a job in days. This
variable can be represented by an integer or a macro (@daily, @weekly,
@monthly), where @daily denotes the same value as the integer 1, @weekly
the same as 7, and @monthly specifies that the job is run once a month,
independent on the length of the month.
.PP
The
.I delay in minutes
variable specifies the number of minutes anacron waits, if necessary,
before executing a job. This variable is represented by an integer where
0 means no delay.
.PP
The
.I job-identifier
variable specifies a unique name of a job which is used in the log files.
.PP
The
.I command
variable specifies the command to execute. The command can either be a
command such as
.B ls /proc >> /tmp/proc
or a command to execute a custom script.
.PP
Environment assignment lines can have the following format:
.PP
VAR=VALUE
.PP
Any spaces around
.I VAR
are removed. No spaces around
.I VALUE
are allowed (unless you want them to be part of the value). The
specified assignment takes effect from the next line until the end of the
file, or to the next assignment of the same variable.
.PP
The
.I START_HOURS_RANGE
variable defines an interval (in hours) when scheduled jobs can be run.
In case this time interval is missed, for example, due to a power down,
then scheduled jobs are not executed that day.
.PP
The
.I RANDOM_DELAY
variable denotes the maximum number of minutes that will be added to the
delay in minutes variable which is specified for each job. A
.I RANDOM_DELAY
set to 12 would therefore add, randomly, between 0 and 12 minutes to the
delay in minutes for each job in that particular anacrontab. When set to
0, no random delay is added.
.PP
If
.I MAILTO
is defined (and non-empty), mail is sent to the specified address,
otherwise, system user is used.
.PP
If
.I MAILFROM
is defined (and non-empty), it is used as the envelope sender address,
otherwise, system user is used.
.PP
(Note: Both
.I MAILFROM
and
.I MAILTO
variables are expanded, so setting them as in the following example works as expected: MAILFROM=cron-$USER@cron.com ($USER is replaced by the system user) )
.PP
.PP
Empty lines are either blank lines, line containing white spaces only, or
lines with white spaces followed by a '#' followed by an arbitrary
comment.
.PP
You can continue a line onto the next line by adding a '\\' at the end of it.
.PP
In case you want to disable Anacron, add a line with
.I 0anacron
which is the name of the script running the Anacron into the
.I /etc/cron.hourly/jobs.deny
file.
.SH EXAMPLE
This example shows how to set up an Anacron job similar in functionality to
.I /etc/crontab
which starts all regular jobs
between 6:00 and 8:00
.I only.
A
.I RANDOM_DELAY
which can be 30 minutes at the most is specified. Jobs will run
serialized in a queue where each job is started only after the previous
one is finished.
.PP
.nf
# environment variables
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
RANDOM_DELAY=30
# Anacron jobs will start between 6am and 8am.
START_HOURS_RANGE=6-8
# delay will be 5 minutes + RANDOM_DELAY for cron.daily
1 5 cron.daily nice run-parts /etc/cron.daily
7 0 cron.weekly nice run-parts /etc/cron.weekly
@monthly 0 cron.monthly nice run-parts /etc/cron.monthly
.fi
.SH "SEE ALSO"
.BR anacron (8),
.BR crontab (1)
.PP
The Anacron
.I README
file.
.SH AUTHOR
.MT itzur@\:actcom.\:co.\:il
Itai Tzur
.ME
.PP
Currently maintained by
.MT pasc@\:(debian.\:org|\:redellipse.\:net)
Pascal Hakim
.ME .
.PP
For Fedora, maintained by
.MT mmaslano@redhat.com
Marcela Mašláňová
.ME .

285
man/cron.8 Normal file
View File

@ -0,0 +1,285 @@
.\"/* Copyright 1988,1990,1993,1996 by Paul Vixie
.\" * All rights reserved
.\" */
.\"
.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
.\"
.\" 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.
.\"
.\" Modified 2010/09/12 by Colin Dean, Durham University IT Service,
.\" to add clustering support.
.\"
.\" $Id: cron.8,v 1.8 2004/01/23 19:03:32 vixie Exp $
.\"
.TH CRON "8" "2013-09-26" "cronie" "System Administration"
.SH NAME
crond \- daemon to execute scheduled commands
.SH SYNOPSIS
.B crond
.RB [ -c " | " -h " | " -i " | " -n " | " -p " | " -P " | " -s " | " -m \fP\fI<mail command>\fP ]
.br
.B crond
.B -x
.RB [ext,sch,proc,pars,load,misc,test,bit]
.br
.B crond
.B -V
.SH DESCRIPTION
.I Cron
is started from
.I /etc/rc.d/init.d
or
.I /etc/init.d
when classical sysvinit scripts are used. In case systemd is enabled, then unit file is installed into
.I /lib/systemd/system/crond.service
and daemon is started by
.I systemctl start crond.service
command. It returns immediately, thus, there is no need to need to start it with
the '&' parameter.
.PP
.I Cron
searches
.I /var/spool/cron
for crontab files which are named after accounts in
.I /etc/passwd;
The found crontabs are loaded into the memory.
.I Cron
also searches for
.I /etc/anacrontab
and any files in the
.I /etc/cron.d
directory, which have a different format (see
.BR crontab (5)).
.I Cron
examines all stored crontabs and checks each job to see if it needs to be
run in the current minute. When executing commands, any output is mailed
to the owner of the crontab (or to the user specified in the
.I MAILTO
environment variable in the crontab, if such exists). Any job output can
also be sent to syslog by using the
.B "\-s"
option.
.PP
There are two ways how changes in crontables are checked. The first
method is checking the modtime of a file. The second method is using the
inotify support. Using of inotify is logged in the
.I /var/log/cron
log after the daemon is started. The inotify support checks for changes
in all crontables and accesses the hard disk only when a change is
detected.
.PP
When using the modtime option,
.I Cron
checks its crontables' modtimes every minute to check for any changes and
reloads the crontables which have changed. There is no need to restart
.I Cron
after some of the crontables were modified. The modtime option is also
used when inotify can not be initialized.
.PP
.I Cron
checks these files and directories:
.TP
.IR /etc/crontab
system crontab. Nowadays the file is empty by default. Originally it
was usually used to run daily, weekly, monthly jobs. By default these
jobs are now run through anacron which reads
.IR /etc/anacrontab
configuration file. See
.BR anacrontab (5)
for more details.
.TP
.IR /etc/cron.d/
directory that contains system cronjobs stored for different users.
.TP
.IR /var/spool/cron
directory that contains user crontables created by the
.IR crontab
command.
.PP
Note that the
.BR crontab (1)
command updates the modtime of the spool directory whenever it changes a
crontab.
.PP
.SS Daylight Saving Time and other time changes
Local time changes of less than three hours, such as those caused by the
Daylight Saving Time changes, are handled in a special way. This only
applies to jobs that run at a specific time and jobs that run with a
granularity greater than one hour. Jobs that run more frequently are
scheduled normally.
.PP
If time was adjusted one hour forward, those jobs that would have run in
the interval that has been skipped will be run immediately. Conversely,
if time was adjusted backward, running the same job twice is avoided.
.PP
Time changes of more than 3 hours are considered to be corrections to the
clock or the timezone, and the new time is used immediately.
.PP
It is possible to use different time zones for crontables. See
.BR crontab (5)
for more information.
.SS PAM Access Control
.IR Cron
supports access control with PAM if the system has PAM installed. For
more information, see
.BR pam (8).
A PAM configuration file for
.IR crond
is installed in
.IR /etc/pam.d/crond .
The daemon loads the PAM environment from the pam_env module. This can
be overridden by defining specific settings in the appropriate crontab
file.
.SH "OPTIONS"
.TP
.B "\-h"
Prints a help message and exits.
.TP
.B "\-i"
Disables inotify support.
.TP
.B "\-m"
This option allows you to specify a shell command to use for sending
.I Cron
mail output instead of using
.BR sendmail (8)
This command must accept a fully formatted mail message (with headers) on
standard input and send it as a mail message to the recipients specified
in the mail headers. Specifying the string
.I "off"
(i.e., crond -m off)
will disable the sending of mail.
.TP
.B "\-n"
Tells the daemon to run in the foreground. This can be useful when
starting it out of init. With this option is needed to change pam setting.
.I /etc/pam.d/crond
must not enable
.I pam_loginuid.so
module.
.TP
.B "\-f"
the same as -n, consistent with other crond implementations.
.TP
.B "\-p"
Allows
.I Cron
to accept any user set crontables.
.TP
.B "\-P"
Don't set PATH. PATH is instead inherited from the environment.
.TP
.B "\-c"
This option enables clustering support, as described below.
.TP
.B "\-s"
This option will direct
.I Cron
to send the job output to the system log using
.BR syslog (3).
This is useful if your system does not have
.BR sendmail (8),
installed or if mail is disabled.
.TP
.B "\-x"
This option allows you to set debug flags.
.TP
.B "\-V"
Print version and exit.
.SH SIGNALS
When the
.I SIGHUP
is received, the
.I Cron
daemon will close and reopen its log file. This proves to be useful in
scripts which rotate and age log files. Naturally, this is not relevant
if
.I Cron
was built to use
.IR syslog (3).
.SH CLUSTERING SUPPORT
In this version of
.IR Cron
it is possible to use a network-mounted shared
.I /var/spool/cron
across a cluster of hosts and specify that only one of the hosts should
run the crontab jobs in this directory at any one time. This is done by
starting
.I Cron
with the
.B \-c
option, and have the
.I /var/spool/cron/.cron.hostname
file contain just one line, which represents the hostname of whichever
host in the cluster should run the jobs. If this file does not exist, or
the hostname in it does not match that returned by
.BR gethostname (2),
then all crontab files in this directory are ignored. This has no effect
on cron jobs specified in the
.I /etc/crontab
file or on files in the
.I /etc/cron.d
directory. These files are always run and considered host-specific.
.PP
Rather than editing
.I /var/spool/cron/.cron.hostname
directly, use the
.B \-n
option of
.BR crontab (1)
to specify the host.
.PP
You should ensure that all hosts in a cluster, and the file server from
which they mount the shared crontab directory, have closely synchronised
clocks, e.g., using
.BR ntpd (8),
otherwise the results will be very unpredictable.
.PP
Using cluster sharing automatically disables inotify support, because
inotify cannot be relied on with network-mounted shared file systems.
.SH CAVEATS
All
.BR crontab
files have to be regular files or symlinks to regular files, they must
not be executable or writable for anyone else but the owner. This
requirement can be overridden by using the
.B \-p
option on the crond command line. If inotify support is in use, changes
in the symlinked crontabs are not automatically noticed by the cron
daemon. The cron daemon must receive a SIGHUP signal to reload the
crontabs. This is a limitation of the inotify API.
.PP
The syslog output will be used instead of mail, when sendmail is not
installed.
.SH "SEE ALSO"
.BR crontab (1),
.BR crontab (5),
.BR inotify (7),
.BR pam (8)
.SH AUTHOR
.MT vixie@isc.org
Paul Vixie
.ME
.br
.MT mmaslano@redhat.com
Marcela Mašláňová
.ME
.br
.MT colin@colin-dean.org
Colin Dean
.ME
.br
.MT tmraz@fedoraproject.org
Tomáš Mráz
.ME

1
man/crond.8 Normal file
View File

@ -0,0 +1 @@
.so man8/cron.8

86
man/cronnext.1 Normal file
View File

@ -0,0 +1,86 @@
.TH CRONNEXT 1 "2017-06-11" "cronie" "User Commands"
.SH NAME
cronnext \- time of next job cron will execute
.SH SYNOPSIS
.TP 9
.B cronnext
[\fB-i \fIusers\fR] [\fB-e \fIusers\fR] [\fB-s\fR]
[\fB-a\fR]
[\fB-t \fItime\fR] [\fB-q \fItime\fR] [\fB-j \fIcommand\fR]
[\fB-l\fR] [\fB-c\fR] [\fB-f\fR] [\fB-h\fR] [\fB-V\fR]
[file]...
.SH DESCRIPTION
Determine the time cron will execute the next job. Without arguments, it
prints that time considering all crontabs, in number of seconds since the
Epoch, rounded to the minute. This number can be converted into other formats
using
.BR date (1),
like
.B date --date @43243254
The file arguments are optional. If provided,
.I cronnext
uses them as crontabs instead of the ones installed in the system.
.SH OPTIONS
.TP
.BI "\-i " user,user,user,...
Consider only the crontabs of the specified users. Use
.B *system*
for the system crontab.
.TP
.BI "\-e " user,user,user,...
Do not consider the crontabs of the specified users.
.TP
.B \-s
Do not consider the system crontab, usually the
.I /etc/crontab
file. The system crontab usually contains the hourly, daily, weekly and
montly crontabs, which might be better dealt with
.BR anacron (8).
.TP
.BI \-a
Use the crontabs installed in the system in addition to the ones passed as
file arguments. This is implicit if no file is passed.
.TP
.BI "\-t " time
Determine the next job from this time, instead of now. The time is
expressed in number of seconds since the Epoch, as obtained for example by
.BR "date +%s --date \(dqnow + 2 hours\(dq" ,
and is internally rounded to the minute.
.TP
.BI "\-q " time
Do not check jobs over this time, expressed in the same way as in option
.BR -t .
.TP
.BI "\-j " command
Only look for jobs that contain \fIcommand\fP as a substring.
.TP
.B \-l
Print the whole entries of the jobs that are the next to be executed by cron.
The default is to only print their next time of execution.
.TP
.B \-c
Print every entry in every crontab with the next time it is executed.
.TP
.B \-f
Print all jobs that are executed in the given interval. Requires option
\fB-q\fR.
.TP
.B \-h
Print usage output and exit.
.TP
.B \-V
Print version and exit.
.SH AUTHOR
.MT sgerwk@aol.com
Marco Migliori
.ME
.SH SEE ALSO
.BR cron (8),
.BR cron (1),
.BR crontab (5),
.BR crontab (1),
.BR anacron (8),
.BR anacrontab (5),
.BR atq (1),
.BR date (1)

251
man/crontab.1 Normal file
View File

@ -0,0 +1,251 @@
.\"/* Copyright 1988,1990,1993 by Paul Vixie
.\" * All rights reserved
.\" */
.\"
.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
.\"
.\" 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.
.\"
.\" Modified 2010/09/12 by Colin Dean, Durham University IT Service,
.\" to add clustering support.
.\"
.\" $Id: crontab.1,v 1.7 2004/01/23 19:03:32 vixie Exp $
.\"
.TH CRONTAB 1 "2019-10-29" "cronie" "User Commands"
.SH NAME
crontab \- maintains crontab files for individual users
.SH SYNOPSIS
.B crontab
.RB [ -u
.IR user ]
.RI < "file"
.RB | \ - >
.br
.B crontab
.RB [ -T ]
.RI < "file"
.RB | \ - >
.br
.B crontab
.RB [ -u
.IR user ]
.RB < -l " | " -r " | " -e >\ [ -i ]
.RB [ -s ]
.br
.B crontab
.BR -n \ [
.IR "hostname " ]
.br
.B crontab
.BR -c
.br
.B crontab
.BR -V
.SH DESCRIPTION
.I Crontab
is the program used to install a crontab table
.IR file ,
remove or list the existing tables used to serve the
.BR cron (8)
daemon. Each user can have their own crontab, and though these are files
in
.IR /var/spool/ ,
they are not intended to be edited directly. For SELinux in MLS mode,
you can define more crontabs for each range. For more information, see
.BR selinux (8).
.PP
In this version of
.IR Cron
it is possible to use a network-mounted shared
.I /var/spool/cron
across a cluster of hosts and specify that only one of the hosts should
run the crontab jobs in the particular directory at any one time. You
may also use
.BR crontab
from any of these hosts to edit the same shared set of crontab files, and
to set and query which host should run the crontab jobs.
.PP
Scheduling cron jobs with
.BR crontab
can be allowed or disallowed for different users. For this purpose, use the
.I cron.allow
and
.I cron.deny
files. If the
.I cron.allow
file exists, a user must be listed in it to be allowed to use
.BR crontab .
If the
.I cron.allow
file does not exist but the
.I cron.deny
file does exist, then a user must
.I not
be listed in the
.I cron.deny
file in order to use
.BR crontab.
If neither of these files exist, then only the super user is allowed to use
.BR crontab .
.PP
Another way to restrict the scheduling of cron jobs beyond
.BR crontab
is to use PAM authentication in
.I /etc/security/access.conf
to set up users, which are allowed or disallowed to use
.BR crontab
or modify system cron jobs in the
.IR /etc/cron.d/
directory.
.PP
The temporary directory can be set in an environment variable. If it is
not set by the user, the
.I /tmp
directory is used.
.PP
When listing a crontab on a terminal the output will be colorized unless
an environment variable
.I NO_COLOR
is set.
.PP
.SH "OPTIONS"
.TP
.B "\-u"
Specifies the name of the user whose crontab is to be modified. If this
option is not used,
.BR crontab
examines "your" crontab, i.e., the crontab of the person executing the
command. If no crontab exists for a particular user, it is created for
them the first time the
.B crontab -u
command is used under their username.
.TP
.B "\-T"
Test the crontab file syntax without installing it.
Once an issue is found, the validation is interrupted, so this will not return all the existing issues at the same execution.
.TP
.B "\-l"
Displays the current crontab on standard output.
.TP
.B "\-r"
Removes the current crontab.
.TP
.B "\-e"
Edits the current crontab using the editor specified by the
.I VISUAL
or
.I EDITOR
environment variables. After you exit from the editor, the modified
crontab will be installed automatically.
.TP
.B "\-i"
This option modifies the
.B "\-r"
option to prompt the user for a 'y/Y' response before actually removing
the crontab.
.TP
.B "\-s"
Appends the current SELinux security context string as an MLS_LEVEL
setting to the crontab file before editing / replacement occurs - see the
documentation of MLS_LEVEL in
.BR crontab (5).
.TP
.B "\-n"
This option is relevant only if
.BR cron (8)
was started with the
.B \-c
option, to enable clustering support. It is used to set the host in the
cluster which should run the jobs specified in the crontab files in the
.I /var/spool/cron
directory. If a hostname is supplied, the host whose hostname returned
by
.BR gethostname (2)
matches the supplied hostname, will be selected to run the selected cron jobs subsequently. If there
is no host in the cluster matching the supplied hostname, or you explicitly specify
an empty hostname, then the selected jobs will not be run at all. If the hostname
is omitted, the name of the local host returned by
.BR gethostname (2)
is used. Using this option has no effect on the
.I /etc/crontab
file and the files in the
.I /etc/cron.d
directory, which are always run, and considered host-specific. For more
information on clustering support, see
.BR cron (8).
.TP
.B "\-c"
This option is only relevant if
.BR cron (8)
was started with the
.B \-c
option, to enable clustering support. It is used to query which host in
the cluster is currently set to run the jobs specified in the crontab
files in the directory
.I /var/spool/cron
, as set using the
.B \-n
option.
.TP
.B "\-V"
Print version and exit.
.SH CAVEATS
The files
.I cron.allow
and
.I cron.deny
cannot be used to restrict the execution of cron jobs; they only restrict the
use of
.BR crontab .
In particular, restricting access to
.BR crontab
has no effect on an existing
.I crontab
of a user. Its jobs will continue to be executed until the crontab is removed.
.PP
The files
.I cron.allow
and
.I cron.deny
must be readable by the user invoking
.BR crontab .
If this is not the case, then they are treated as non-existent.
.SH "SEE ALSO"
.BR crontab (5),
.BR cron (8)
.SH FILES
.nf
/etc/cron.allow
/etc/cron.deny
.fi
.SH STANDARDS
The
.I crontab
command conforms to IEEE Std1003.2-1992 (``POSIX'') with one exception:
For replacing the current crontab with data from standard input the
.B \-
has to be specified on the command line if the standard input is a TTY.
This new command syntax differs from previous versions of Vixie Cron,
as well as from the classic SVR3 syntax.
.SH DIAGNOSTICS
An informative usage message appears if you run a crontab with a faulty
command defined in it.
.SH AUTHOR
.MT vixie@isc.org
Paul Vixie
.ME
.br
.MT colin@colin-dean.org
Colin Dean
.ME

357
man/crontab.5 Normal file
View File

@ -0,0 +1,357 @@
.\"/* Copyright 1988,1990,1993,1994 by Paul Vixie
.\" * All rights reserved
.\" */
.\"
.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
.\"
.\" 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.
.\"
.\" $Id: crontab.5,v 1.6 2004/01/23 19:03:33 vixie Exp $
.\"
.TH CRONTAB 5 2012-11-22 "cronie" "File Formats"
.SH NAME
crontab \- files used to schedule the execution of programs
.SH DESCRIPTION
A
.I crontab
file contains instructions for the
.BR cron (8)
daemon in the following simplified manner: "run this command at this time
on this date". Each user can define their own crontab. Commands defined
in any given crontab are executed under the user who owns that particular
crontab. Uucp and News usually have their own crontabs, eliminating the
need for explicitly running
.BR su (1)
as part of a cron command.
.PP
Blank lines, leading spaces, and tabs are ignored. Lines whose first
non-white space character is a pound-sign (#) are comments, and are not
processed. Note that comments are not allowed on the same line as cron
commands, since they are considered a part of the command. Similarly,
comments are not allowed on the same line as environment variable
settings.
.PP
An active line in a crontab is either an environment setting or a cron
command. An environment setting is of the form:
.PP
name = value
.PP
where the white spaces around the equal-sign (=) are optional, and any
subsequent non-leading white spaces in
.I value
is a part of the value assigned to
.IR name .
The
.I value
string may be placed in quotes (single or double, but matching) to
preserve leading or trailing white spaces.
.PP
Several environment variables are set up automatically by the
.BR cron (8)
daemon.
.I SHELL
is set to /bin/sh, and
.I LOGNAME
and
.I HOME
are set from the /etc/passwd line of the crontab\'s owner.
.I HOME
and
.I SHELL
can be overridden by settings in the crontab; LOGNAME can not.
.PP
(Note: the
.I LOGNAME
variable is sometimes called
.I USER
on BSD systems and is also automatically set).
.PP
In addition to
.IR LOGNAME ,
.IR HOME ,
and
.IR SHELL ,
.BR cron (8)
looks at the
.I MAILTO
variable if a mail needs to be send as a result of running any commands
in that particular crontab. If
.I MAILTO
is defined (and non-empty), mail is sent to the specified address. If
.I MAILTO
is defined but empty
.RI ( MAILTO="" ),
no mail is sent. Otherwise, mail is sent to the owner of the crontab.
This option is useful if you decide to use /bin/mail instead of
/usr/lib/sendmail as your mailer. Note that /bin/mail does not provide
aliasing and UUCP usually does not read its mail. If
.I MAILFROM
is defined (and non-empty), it is used as the envelope sender address,
otherwise, ``root'' is used.
.PP
(Note: Both
.I MAILFROM
and
.I MAILTO
variables are expanded, so setting them as in the following example works as expected: MAILFROM=cron-$USER@cron.com ($USER is replaced by the system user) )
.PP
By default, cron sends a mail using the 'Content-Type:' header
of 'text/plain' with the 'charset=' parameter set to the 'charmap/codeset'
of the locale in which
.BR crond (8)
is started up, i.e., either the default system locale, if no LC_*
environment variables are set, or the locale specified by the LC_*
environment variables (see
.BR locale (7)).
Different character encodings can be used for mailing cron job outputs by
setting the
.I CONTENT_TYPE
and
.I CONTENT_TRANSFER_ENCODING
variables in a crontab to the correct values of the mail headers of those
names.
.PP
The
.I CRON_TZ
variable specifies the time zone specific for the cron table. The user
should enter a time according to the specified time zone into the table.
The time used for writing into a log file is taken from the local time
zone, where the daemon is running.
.PP
The
.I MLS_LEVEL
environment variable provides support for multiple per-job SELinux
security contexts in the same crontab. By default, cron jobs execute
with the default SELinux security context of the user that created the
crontab file. When using multiple security levels and roles, this may
not be sufficient, because the same user may be running in different
roles or in different security levels. For more information about roles
and SELinux MLS/MCS, see
.BR selinux (8)
and the crontab example mentioned later on in this text. You can set the
.I MLS_LEVEL
variable to the SELinux security context string specifying the particular
SELinux security context in which you want jobs to be run.
.B crond
will then set the execution context of those jobs that meet the
specifications of the particular security context. For more information,
see
.BR crontab (1)\ -s\ option.
.PP
The
.I RANDOM_DELAY
variable allows delaying job startups by random amount of minutes with
upper limit specified by the variable. The random scaling factor is
determined during the cron daemon startup so it remains constant for
the whole run time of the daemon.
.PP
The format of a cron command is similar to the V7 standard, with a number
of upward-compatible extensions. Each line has five time-and-date fields
followed by a
.BR user name
(if this is the
.BR system
crontab file), and followed by a command. Commands are executed by
.BR cron (8)
when the 'minute', 'hour', and 'month of the year' fields match the
current time,
.I and
at least one of the two 'day' fields ('day of month', or 'day of week')
match the current time (see "Note" below).
.PP
Note that this means that non-existent times, such as the "missing hours"
during the daylight savings time conversion, will never match, causing
jobs scheduled during the "missing times" not to be run. Similarly,
times that occur more than once (again, during the daylight savings time
conversion) will cause matching jobs to be run twice.
.PP
.BR cron (8)
examines cron entries every minute.
.PP
The time and date fields are:
.IP
.ta 1.5i
field allowed values
.br
----- --------------
.br
minute 0-59
.br
hour 0-23
.br
day of month 1-31
.br
month 1-12 (or names, see below)
.br
day of week 0-7 (0 or 7 is Sunday, or use names)
.br
.PP
A field may contain an asterisk (*), which always stands for
"first\-last".
.PP
Ranges of numbers are allowed. Ranges are two numbers separated with a
hyphen. The specified range is inclusive. For example, 8-11 for
an 'hours' entry specifies execution at hours 8, 9, 10, and 11. The first
number must be less than or equal to the second one.
.PP
Randomization of the execution time within a range can be used.
A random number within a range specified as two numbers separated with
a tilde is picked. The specified range is inclusive.
For example, 6~15 for a 'minutes' entry picks a random minute
within 6 to 15 range. The random number is picked when crontab file is parsed.
The first number must be less than or equal to the second one. You might omit
one or both of the numbers specifying the range. For example, ~ for a 'minutes'
entry picks a random minute within 0 to 59 range.
.PP
Lists are allowed. A list is a set of numbers (or ranges) separated by
commas. Examples: "1,2,5,9", "0-4,8-12".
.PP
Step values can be used in conjunction with ranges. Following a range
with "/<number>" specifies skips of the number's value through the range.
For example, "0-23/2" can be used in the 'hours' field to specify command
execution for every other hour (the alternative in the V7 standard is
"0,\:2,\:4,\:6,\:8,\:10,\:12,\:14,\:16,\:18,\:20,\:22"). Step values are
also permitted after an asterisk, so if specifying a job to be run every
two hours, you can use "*/2".
.PP
Names can also be used for the 'month' and 'day of week' fields. Use the
first three letters of the particular day or month (case does not
matter). Ranges and lists of names are allowed. Examples: "mon,wed,fri",
"jan-mar".
.PP
If the UID of the owner is 0 (root), the first character of a crontab
entry can be "-" character. This will prevent cron from writing a syslog
message about the command being executed.
.PP
The "sixth" field (the rest of the line) specifies the command to be run.
The entire command portion of the line, up to a newline or a "%"
character, will be executed by /bin/sh or by the shell specified in the
SHELL variable of the cronfile. A "%" character in the command, unless
escaped with a backslash (\\), will be changed into newline characters,
and all data after the first % will be sent to the command as standard
input.
.PP
Note: The day of a command's execution can be specified in the following
two fields \(em 'day of month', and 'day of week'. If both fields are
restricted (i.e., do not contain the "*" character), the command will be
run when
.I either
field matches the current time. For example,
.br
"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.
.PP
A crontab file syntax can be tested before an install using the -T option. See
.BR crontab (1)
for details.
.SH EXAMPLE CRON FILE
.nf
# use /bin/sh to run commands, no matter what /etc/passwd says
SHELL=/bin/sh
# mail any output to `paul', no matter whose crontab this is
MAILTO=paul
#
CRON_TZ=Japan
# run five minutes after midnight, every day
5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
# run at 2:15pm on the first of every month -- output mailed to paul
15 14 1 * * $HOME/bin/monthly
# run at 10 pm on weekdays, annoy Joe
0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?%
23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday"
5 4 * * sun echo "run at 5 after 4 every sunday"
.fi
.SH Jobs in /etc/cron.d/
The jobs in
.I cron.d
and
.I /etc/crontab
are system jobs, which are used usually for more than one user, thus,
additionally the username is needed. MAILTO on the first line is
optional.
.SH EXAMPLE OF A JOB IN /etc/cron.d/job
.nf
#login as root
#create job with preferred editor (e.g. vim)
MAILTO=root
* * * * * root touch /tmp/file
.fi
.SH SELinux with multi level security (MLS)
In a crontab, it is important to specify a security level by
.I crontab \-s
or specifying the required level on the first line of the crontab. Each
level is specified in
.IR /etc/selinux/targeted/seusers .
When using crontab in the MLS mode, it is especially important to:
.br
- check/change the actual role,
.br
- set correct
.I role for
.IR directory ,
which is used for input/output.
.SH EXAMPLE FOR SELINUX MLS
.nf
# login as root
newrole -r sysadm_r
mkdir /tmp/SystemHigh
chcon -l SystemHigh /tmp/SystemHigh
crontab -e
# write in crontab file
MLS_LEVEL=SystemHigh
0-59 * * * * id -Z > /tmp/SystemHigh/crontest
.fi
.SH FILES
.I /etc/crontab
main system crontab file.
.I /var/spool/cron/
a directory for storing crontabs defined by users.
.I /etc/cron.d/
a directory for storing system crontabs.
.SH "SEE ALSO"
.BR cron (8),
.BR crontab (1)
.SH EXTENSIONS
These special time specification "nicknames" which replace the 5 initial
time and date fields, and are prefixed with the '@' character, are
supported:
.PP
.nf
@reboot : Run once after reboot.
@yearly : Run once a year, ie. "0 0 1 1 *".
@annually : Run once a year, ie. "0 0 1 1 *".
@monthly : Run once a month, ie. "0 0 1 * *".
@weekly : Run once a week, ie. "0 0 * * 0".
@daily : Run once a day, ie. "0 0 * * *".
@hourly : Run once an hour, ie. "0 * * * *".
.fi
.SH CAVEATS
.BR crontab
files have to be regular files or symlinks to regular files, they must
not be executable or writable for anyone else but the owner. This
requirement can be overridden by using the
.B \-p
option on the crond command line. If inotify support is in use, changes
in the symlinked crontabs are not automatically noticed by the cron
daemon. The cron daemon must receive a SIGHUP signal to reload the
crontabs. This is a limitation of the inotify API.
.PP
cron requires that each entry in a crontab end in a newline character. If the
last entry in a crontab is missing a newline (i.e.\& terminated by EOF),
cron will consider the crontab (at least partially) broken.
A warning will be written to syslog.
.SH AUTHOR
.MT vixie@isc.org
Paul Vixie
.ME

215
missing Executable file
View File

@ -0,0 +1,215 @@
#! /bin/sh
# Common wrapper for a few potentially missing GNU programs.
scriptversion=2018-03-07.03; # UTC
# Copyright (C) 1996-2018 Free Software Foundation, Inc.
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# 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, 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, see <https://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
if test $# -eq 0; then
echo 1>&2 "Try '$0 --help' for more information"
exit 1
fi
case $1 in
--is-lightweight)
# Used by our autoconf macros to check whether the available missing
# script is modern enough.
exit 0
;;
--run)
# Back-compat with the calling convention used by older automake.
shift
;;
-h|--h|--he|--hel|--help)
echo "\
$0 [OPTION]... PROGRAM [ARGUMENT]...
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
to PROGRAM being missing or too old.
Options:
-h, --help display this help and exit
-v, --version output version information and exit
Supported PROGRAM values:
aclocal autoconf autoheader autom4te automake makeinfo
bison yacc flex lex help2man
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
'g' are ignored when checking the name.
Send bug reports to <bug-automake@gnu.org>."
exit $?
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing $scriptversion (GNU Automake)"
exit $?
;;
-*)
echo 1>&2 "$0: unknown '$1' option"
echo 1>&2 "Try '$0 --help' for more information"
exit 1
;;
esac
# Run the given program, remember its exit status.
"$@"; st=$?
# If it succeeded, we are done.
test $st -eq 0 && exit 0
# Also exit now if we it failed (or wasn't found), and '--version' was
# passed; such an option is passed most likely to detect whether the
# program is present and works.
case $2 in --version|--help) exit $st;; esac
# Exit code 63 means version mismatch. This often happens when the user
# tries to use an ancient version of a tool on a file that requires a
# minimum version.
if test $st -eq 63; then
msg="probably too old"
elif test $st -eq 127; then
# Program was missing.
msg="missing on your system"
else
# Program was found and executed, but failed. Give up.
exit $st
fi
perl_URL=https://www.perl.org/
flex_URL=https://github.com/westes/flex
gnu_software_URL=https://www.gnu.org/software
program_details ()
{
case $1 in
aclocal|automake)
echo "The '$1' program is part of the GNU Automake package:"
echo "<$gnu_software_URL/automake>"
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/autoconf>"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
autoconf|autom4te|autoheader)
echo "The '$1' program is part of the GNU Autoconf package:"
echo "<$gnu_software_URL/autoconf/>"
echo "It also requires GNU m4 and Perl in order to run:"
echo "<$gnu_software_URL/m4/>"
echo "<$perl_URL>"
;;
esac
}
give_advice ()
{
# Normalize program name to check for.
normalized_program=`echo "$1" | sed '
s/^gnu-//; t
s/^gnu//; t
s/^g//; t'`
printf '%s\n' "'$1' is $msg."
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
case $normalized_program in
autoconf*)
echo "You should only need it if you modified 'configure.ac',"
echo "or m4 files included by it."
program_details 'autoconf'
;;
autoheader*)
echo "You should only need it if you modified 'acconfig.h' or"
echo "$configure_deps."
program_details 'autoheader'
;;
automake*)
echo "You should only need it if you modified 'Makefile.am' or"
echo "$configure_deps."
program_details 'automake'
;;
aclocal*)
echo "You should only need it if you modified 'acinclude.m4' or"
echo "$configure_deps."
program_details 'aclocal'
;;
autom4te*)
echo "You might have modified some maintainer files that require"
echo "the 'autom4te' program to be rebuilt."
program_details 'autom4te'
;;
bison*|yacc*)
echo "You should only need it if you modified a '.y' file."
echo "You may want to install the GNU Bison package:"
echo "<$gnu_software_URL/bison/>"
;;
lex*|flex*)
echo "You should only need it if you modified a '.l' file."
echo "You may want to install the Fast Lexical Analyzer package:"
echo "<$flex_URL>"
;;
help2man*)
echo "You should only need it if you modified a dependency" \
"of a man page."
echo "You may want to install the GNU Help2man package:"
echo "<$gnu_software_URL/help2man/>"
;;
makeinfo*)
echo "You should only need it if you modified a '.texi' file, or"
echo "any other file indirectly affecting the aspect of the manual."
echo "You might want to install the Texinfo package:"
echo "<$gnu_software_URL/texinfo/>"
echo "The spurious makeinfo call might also be the consequence of"
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
echo "want to install GNU make:"
echo "<$gnu_software_URL/make/>"
;;
*)
echo "You might have modified some files without having the proper"
echo "tools for further handling them. Check the 'README' file, it"
echo "often tells you about the needed prerequisites for installing"
echo "this package. You may also peek at any GNU archive site, in"
echo "case some other package contains this missing '$1' program."
;;
esac
}
give_advice "$1" | sed -e '1s/^/WARNING: /' \
-e '2,$s/^/ /' >&2
# Propagate the correct exit status (expected to be 127 for a program
# not found, 63 for a program that failed due to version mismatch).
exit $st
# Local variables:
# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:

376
obstack/obstack.c Normal file
View File

@ -0,0 +1,376 @@
/* obstack.c - subroutines used implicitly by object stack macros
Copyright (C) 1988-2020 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
#ifdef _LIBC
# include <obstack.h>
#else
# include <config.h>
# include "obstack.h"
#endif
/* NOTE BEFORE MODIFYING THIS FILE: _OBSTACK_INTERFACE_VERSION in
obstack.h must be incremented whenever callers compiled using an old
obstack.h can no longer properly call the functions in this file. */
/* Comment out all this code if we are using the GNU C Library, and are not
actually compiling the library itself, and the installed library
supports the same library interface we do. This code is part of the GNU
C Library, but also included in many other GNU distributions. Compiling
and linking in this code is a waste when using the GNU C library
(especially if it is a shared library). Rather than having every GNU
program understand 'configure --with-gnu-libc' and omit the object
files, it is simpler to just do this in the source for each such file. */
#if !defined _LIBC && defined __GNU_LIBRARY__ && __GNU_LIBRARY__ > 1
# include <gnu-versions.h>
# if (_GNU_OBSTACK_INTERFACE_VERSION == _OBSTACK_INTERFACE_VERSION \
|| (_GNU_OBSTACK_INTERFACE_VERSION == 1 \
&& _OBSTACK_INTERFACE_VERSION == 2 \
&& defined SIZEOF_INT && defined SIZEOF_SIZE_T \
&& SIZEOF_INT == SIZEOF_SIZE_T))
# define _OBSTACK_ELIDE_CODE
# endif
#endif
#ifndef _OBSTACK_ELIDE_CODE
/* If GCC, or if an oddball (testing?) host that #defines __alignof__,
use the already-supplied __alignof__. Otherwise, this must be Gnulib
(as glibc assumes GCC); defer to Gnulib's alignof_type. */
# if !defined __GNUC__ && !defined __IBM__ALIGNOF__ && !defined __alignof__
# if defined __cplusplus
template <class type> struct alignof_helper { char __slot1; type __slot2; };
# define __alignof__(type) offsetof (alignof_helper<type>, __slot2)
# else
# define __alignof__(type) \
offsetof (struct { char __slot1; type __slot2; }, __slot2)
# endif
# endif
# include <stdlib.h>
# include <stdint.h>
# ifndef MAX
# define MAX(a,b) ((a) > (b) ? (a) : (b))
# endif
/* Determine default alignment. */
/* If malloc were really smart, it would round addresses to DEFAULT_ALIGNMENT.
But in fact it might be less smart and round addresses to as much as
DEFAULT_ROUNDING. So we prepare for it to do that.
DEFAULT_ALIGNMENT cannot be an enum constant; see gnulib's alignof.h. */
#define DEFAULT_ALIGNMENT MAX (__alignof__ (long double), \
MAX (__alignof__ (uintmax_t), \
__alignof__ (void *)))
#define DEFAULT_ROUNDING MAX (sizeof (long double), \
MAX (sizeof (uintmax_t), \
sizeof (void *)))
/* Call functions with either the traditional malloc/free calling
interface, or the mmalloc/mfree interface (that adds an extra first
argument), based on the value of use_extra_arg. */
static void *
call_chunkfun (struct obstack *h, size_t size)
{
if (h->use_extra_arg)
return h->chunkfun.extra (h->extra_arg, size);
else
return h->chunkfun.plain (size);
}
static void
call_freefun (struct obstack *h, void *old_chunk)
{
if (h->use_extra_arg)
h->freefun.extra (h->extra_arg, old_chunk);
else
h->freefun.plain (old_chunk);
}
/* Initialize an obstack H for use. Specify chunk size SIZE (0 means default).
Objects start on multiples of ALIGNMENT (0 means use default).
Return nonzero if successful, calls obstack_alloc_failed_handler if
allocation fails. */
static int
_obstack_begin_worker (struct obstack *h,
_OBSTACK_SIZE_T size, _OBSTACK_SIZE_T alignment)
{
struct _obstack_chunk *chunk; /* points to new chunk */
if (alignment == 0)
alignment = DEFAULT_ALIGNMENT;
if (size == 0)
/* Default size is what GNU malloc can fit in a 4096-byte block. */
{
/* 12 is sizeof (mhead) and 4 is EXTRA from GNU malloc.
Use the values for range checking, because if range checking is off,
the extra bytes won't be missed terribly, but if range checking is on
and we used a larger request, a whole extra 4096 bytes would be
allocated.
These number are irrelevant to the new GNU malloc. I suspect it is
less sensitive to the size of the request. */
int extra = ((((12 + DEFAULT_ROUNDING - 1) & ~(DEFAULT_ROUNDING - 1))
+ 4 + DEFAULT_ROUNDING - 1)
& ~(DEFAULT_ROUNDING - 1));
size = 4096 - extra;
}
h->chunk_size = size;
h->alignment_mask = alignment - 1;
chunk = (struct _obstack_chunk *) call_chunkfun (h, h->chunk_size);
if (!chunk)
(*obstack_alloc_failed_handler) ();
h->chunk = chunk;
h->next_free = h->object_base = __PTR_ALIGN ((char *) chunk, chunk->contents,
alignment - 1);
h->chunk_limit = chunk->limit = (char *) chunk + h->chunk_size;
chunk->prev = 0;
/* The initial chunk now contains no empty object. */
h->maybe_empty_object = 0;
h->alloc_failed = 0;
return 1;
}
int
_obstack_begin (struct obstack *h,
_OBSTACK_SIZE_T size, _OBSTACK_SIZE_T alignment,
void *(*chunkfun) (size_t),
void (*freefun) (void *))
{
h->chunkfun.plain = chunkfun;
h->freefun.plain = freefun;
h->use_extra_arg = 0;
return _obstack_begin_worker (h, size, alignment);
}
int
_obstack_begin_1 (struct obstack *h,
_OBSTACK_SIZE_T size, _OBSTACK_SIZE_T alignment,
void *(*chunkfun) (void *, size_t),
void (*freefun) (void *, void *),
void *arg)
{
h->chunkfun.extra = chunkfun;
h->freefun.extra = freefun;
h->extra_arg = arg;
h->use_extra_arg = 1;
return _obstack_begin_worker (h, size, alignment);
}
/* Allocate a new current chunk for the obstack *H
on the assumption that LENGTH bytes need to be added
to the current object, or a new object of length LENGTH allocated.
Copies any partial object from the end of the old chunk
to the beginning of the new one. */
void
_obstack_newchunk (struct obstack *h, _OBSTACK_SIZE_T length)
{
struct _obstack_chunk *old_chunk = h->chunk;
struct _obstack_chunk *new_chunk = 0;
size_t obj_size = h->next_free - h->object_base;
char *object_base;
/* Compute size for new chunk. */
size_t sum1 = obj_size + length;
size_t sum2 = sum1 + h->alignment_mask;
size_t new_size = sum2 + (obj_size >> 3) + 100;
if (new_size < sum2)
new_size = sum2;
if (new_size < h->chunk_size)
new_size = h->chunk_size;
/* Allocate and initialize the new chunk. */
if (obj_size <= sum1 && sum1 <= sum2)
new_chunk = (struct _obstack_chunk *) call_chunkfun (h, new_size);
if (!new_chunk)
(*obstack_alloc_failed_handler)();
h->chunk = new_chunk;
new_chunk->prev = old_chunk;
new_chunk->limit = h->chunk_limit = (char *) new_chunk + new_size;
/* Compute an aligned object_base in the new chunk */
object_base =
__PTR_ALIGN ((char *) new_chunk, new_chunk->contents, h->alignment_mask);
/* Move the existing object to the new chunk. */
memcpy (object_base, h->object_base, obj_size);
/* If the object just copied was the only data in OLD_CHUNK,
free that chunk and remove it from the chain.
But not if that chunk might contain an empty object. */
if (!h->maybe_empty_object
&& (h->object_base
== __PTR_ALIGN ((char *) old_chunk, old_chunk->contents,
h->alignment_mask)))
{
new_chunk->prev = old_chunk->prev;
call_freefun (h, old_chunk);
}
h->object_base = object_base;
h->next_free = h->object_base + obj_size;
/* The new chunk certainly contains no empty object yet. */
h->maybe_empty_object = 0;
}
/* Return nonzero if object OBJ has been allocated from obstack H.
This is here for debugging.
If you use it in a program, you are probably losing. */
/* Suppress -Wmissing-prototypes warning. We don't want to declare this in
obstack.h because it is just for debugging. */
int _obstack_allocated_p (struct obstack *h, void *obj) __attribute_pure__;
int
_obstack_allocated_p (struct obstack *h, void *obj)
{
struct _obstack_chunk *lp; /* below addr of any objects in this chunk */
struct _obstack_chunk *plp; /* point to previous chunk if any */
lp = (h)->chunk;
/* We use >= rather than > since the object cannot be exactly at
the beginning of the chunk but might be an empty object exactly
at the end of an adjacent chunk. */
while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj))
{
plp = lp->prev;
lp = plp;
}
return lp != 0;
}
/* Free objects in obstack H, including OBJ and everything allocate
more recently than OBJ. If OBJ is zero, free everything in H. */
void
_obstack_free (struct obstack *h, void *obj)
{
struct _obstack_chunk *lp; /* below addr of any objects in this chunk */
struct _obstack_chunk *plp; /* point to previous chunk if any */
lp = h->chunk;
/* We use >= because there cannot be an object at the beginning of a chunk.
But there can be an empty object at that address
at the end of another chunk. */
while (lp != 0 && ((void *) lp >= obj || (void *) (lp)->limit < obj))
{
plp = lp->prev;
call_freefun (h, lp);
lp = plp;
/* If we switch chunks, we can't tell whether the new current
chunk contains an empty object, so assume that it may. */
h->maybe_empty_object = 1;
}
if (lp)
{
h->object_base = h->next_free = (char *) (obj);
h->chunk_limit = lp->limit;
h->chunk = lp;
}
else if (obj != 0)
/* obj is not in any of the chunks! */
abort ();
}
_OBSTACK_SIZE_T
_obstack_memory_used (struct obstack *h)
{
struct _obstack_chunk *lp;
_OBSTACK_SIZE_T nbytes = 0;
for (lp = h->chunk; lp != 0; lp = lp->prev)
{
nbytes += lp->limit - (char *) lp;
}
return nbytes;
}
# ifndef _OBSTACK_NO_ERROR_HANDLER
/* Define the error handler. */
# include <stdio.h>
/* Exit value used when 'print_and_abort' is used. */
# ifdef _LIBC
int obstack_exit_failure = EXIT_FAILURE;
# else
# ifndef EXIT_FAILURE
# define EXIT_FAILURE 1
# endif
# define obstack_exit_failure EXIT_FAILURE
# endif
# if defined _LIBC || (HAVE_LIBINTL_H && ENABLE_NLS)
# include <libintl.h>
# ifndef _
# define _(msgid) gettext (msgid)
# endif
# else
# ifndef _
# define _(msgid) (msgid)
# endif
# endif
# if !(defined _Noreturn \
|| (defined __STDC_VERSION__ && __STDC_VERSION__ >= 201112))
# if ((defined __GNUC__ \
&& (__GNUC__ >= 3 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))) \
|| (defined __SUNPRO_C && __SUNPRO_C >= 0x5110))
# define _Noreturn __attribute__ ((__noreturn__))
# elif defined _MSC_VER && _MSC_VER >= 1200
# define _Noreturn __declspec (noreturn)
# else
# define _Noreturn
# endif
# endif
# ifdef _LIBC
# include <libio/iolibio.h>
# endif
static _Noreturn void
print_and_abort (void)
{
/* Don't change any of these strings. Yes, it would be possible to add
the newline to the string and use fputs or so. But this must not
happen because the "memory exhausted" message appears in other places
like this and the translation should be reused instead of creating
a very similar string which requires a separate translation. */
# ifdef _LIBC
(void) __fxprintf (NULL, "%s\n", _("memory exhausted"));
# else
fprintf (stderr, "%s\n", _("memory exhausted"));
# endif
exit (obstack_exit_failure);
}
/* The functions allocating more room by calling 'obstack_chunk_alloc'
jump to the handler pointed to by 'obstack_alloc_failed_handler'.
This can be set to a user defined function which should either
abort gracefully or use longjump - but shouldn't return. This
variable by default points to the internal function
'print_and_abort'. */
void (*obstack_alloc_failed_handler) (void) = print_and_abort;
# endif /* !_OBSTACK_NO_ERROR_HANDLER */
#endif /* !_OBSTACK_ELIDE_CODE */

535
obstack/obstack.h Normal file
View File

@ -0,0 +1,535 @@
/* obstack.h - object stack macros
Copyright (C) 1988-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* Summary:
All the apparent functions defined here are macros. The idea
is that you would use these pre-tested macros to solve a
very specific set of problems, and they would run fast.
Caution: no side-effects in arguments please!! They may be
evaluated MANY times!!
These macros operate a stack of objects. Each object starts life
small, and may grow to maturity. (Consider building a word syllable
by syllable.) An object can move while it is growing. Once it has
been "finished" it never changes address again. So the "top of the
stack" is typically an immature growing object, while the rest of the
stack is of mature, fixed size and fixed address objects.
These routines grab large chunks of memory, using a function you
supply, called 'obstack_chunk_alloc'. On occasion, they free chunks,
by calling 'obstack_chunk_free'. You must define them and declare
them before using any obstack macros.
Each independent stack is represented by a 'struct obstack'.
Each of the obstack macros expects a pointer to such a structure
as the first argument.
One motivation for this package is the problem of growing char strings
in symbol tables. Unless you are "fascist pig with a read-only mind"
--Gosper's immortal quote from HAKMEM item 154, out of context--you
would not like to put any arbitrary upper limit on the length of your
symbols.
In practice this often means you will build many short symbols and a
few long symbols. At the time you are reading a symbol you don't know
how long it is. One traditional method is to read a symbol into a
buffer, realloc()ating the buffer every time you try to read a symbol
that is longer than the buffer. This is beaut, but you still will
want to copy the symbol from the buffer to a more permanent
symbol-table entry say about half the time.
With obstacks, you can work differently. Use one obstack for all symbol
names. As you read a symbol, grow the name in the obstack gradually.
When the name is complete, finalize it. Then, if the symbol exists already,
free the newly read name.
The way we do this is to take a large chunk, allocating memory from
low addresses. When you want to build a symbol in the chunk you just
add chars above the current "high water mark" in the chunk. When you
have finished adding chars, because you got to the end of the symbol,
you know how long the chars are, and you can create a new object.
Mostly the chars will not burst over the highest address of the chunk,
because you would typically expect a chunk to be (say) 100 times as
long as an average object.
In case that isn't clear, when we have enough chars to make up
the object, THEY ARE ALREADY CONTIGUOUS IN THE CHUNK (guaranteed)
so we just point to it where it lies. No moving of chars is
needed and this is the second win: potentially long strings need
never be explicitly shuffled. Once an object is formed, it does not
change its address during its lifetime.
When the chars burst over a chunk boundary, we allocate a larger
chunk, and then copy the partly formed object from the end of the old
chunk to the beginning of the new larger chunk. We then carry on
accreting characters to the end of the object as we normally would.
A special macro is provided to add a single char at a time to a
growing object. This allows the use of register variables, which
break the ordinary 'growth' macro.
Summary:
We allocate large chunks.
We carve out one object at a time from the current chunk.
Once carved, an object never moves.
We are free to append data of any size to the currently
growing object.
Exactly one object is growing in an obstack at any one time.
You can run one obstack per control block.
You may have as many control blocks as you dare.
Because of the way we do it, you can "unwind" an obstack
back to a previous state. (You may remove objects much
as you would with a stack.)
*/
/* Don't do the contents of this file more than once. */
#ifndef _OBSTACK_H
#define _OBSTACK_H 1
#ifndef _OBSTACK_INTERFACE_VERSION
# define _OBSTACK_INTERFACE_VERSION 2
#endif
#include <stddef.h> /* For size_t and ptrdiff_t. */
#include <string.h> /* For __GNU_LIBRARY__, and memcpy. */
#if _OBSTACK_INTERFACE_VERSION == 1
/* For binary compatibility with obstack version 1, which used "int"
and "long" for these two types. */
# define _OBSTACK_SIZE_T unsigned int
# define _CHUNK_SIZE_T unsigned long
# define _OBSTACK_CAST(type, expr) ((type) (expr))
#else
/* Version 2 with sane types, especially for 64-bit hosts. */
# define _OBSTACK_SIZE_T size_t
# define _CHUNK_SIZE_T size_t
# define _OBSTACK_CAST(type, expr) (expr)
#endif
/* If B is the base of an object addressed by P, return the result of
aligning P to the next multiple of A + 1. B and P must be of type
char *. A + 1 must be a power of 2. */
#define __BPTR_ALIGN(B, P, A) ((B) + (((P) - (B) + (A)) & ~(A)))
/* Similar to __BPTR_ALIGN (B, P, A), except optimize the common case
where pointers can be converted to integers, aligned as integers,
and converted back again. If ptrdiff_t is narrower than a
pointer (e.g., the AS/400), play it safe and compute the alignment
relative to B. Otherwise, use the faster strategy of computing the
alignment relative to 0. */
#define __PTR_ALIGN(B, P, A) \
__BPTR_ALIGN (sizeof (ptrdiff_t) < sizeof (void *) ? (B) : (char *) 0, \
P, A)
#ifndef __attribute_pure__
# if defined __GNUC_MINOR__ && __GNUC__ * 1000 + __GNUC_MINOR__ >= 2096
# define __attribute_pure__ __attribute__ ((__pure__))
# else
# define __attribute_pure__
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct _obstack_chunk /* Lives at front of each chunk. */
{
char *limit; /* 1 past end of this chunk */
struct _obstack_chunk *prev; /* address of prior chunk or NULL */
char contents[4]; /* objects begin here */
};
struct obstack /* control current object in current chunk */
{
_CHUNK_SIZE_T chunk_size; /* preferred size to allocate chunks in */
struct _obstack_chunk *chunk; /* address of current struct obstack_chunk */
char *object_base; /* address of object we are building */
char *next_free; /* where to add next char to current object */
char *chunk_limit; /* address of char after current chunk */
union
{
_OBSTACK_SIZE_T i;
void *p;
} temp; /* Temporary for some macros. */
_OBSTACK_SIZE_T alignment_mask; /* Mask of alignment for each object. */
/* These prototypes vary based on 'use_extra_arg'. */
union
{
void *(*plain) (size_t);
void *(*extra) (void *, size_t);
} chunkfun;
union
{
void (*plain) (void *);
void (*extra) (void *, void *);
} freefun;
void *extra_arg; /* first arg for chunk alloc/dealloc funcs */
unsigned use_extra_arg : 1; /* chunk alloc/dealloc funcs take extra arg */
unsigned maybe_empty_object : 1; /* There is a possibility that the current
chunk contains a zero-length object. This
prevents freeing the chunk if we allocate
a bigger chunk to replace it. */
unsigned alloc_failed : 1; /* No longer used, as we now call the failed
handler on error, but retained for binary
compatibility. */
};
/* Declare the external functions we use; they are in obstack.c. */
extern void _obstack_newchunk (struct obstack *, _OBSTACK_SIZE_T);
extern void _obstack_free (struct obstack *, void *);
extern int _obstack_begin (struct obstack *,
_OBSTACK_SIZE_T, _OBSTACK_SIZE_T,
void *(*) (size_t), void (*) (void *));
extern int _obstack_begin_1 (struct obstack *,
_OBSTACK_SIZE_T, _OBSTACK_SIZE_T,
void *(*) (void *, size_t),
void (*) (void *, void *), void *);
extern _OBSTACK_SIZE_T _obstack_memory_used (struct obstack *)
__attribute_pure__;
/* Error handler called when 'obstack_chunk_alloc' failed to allocate
more memory. This can be set to a user defined function which
should either abort gracefully or use longjump - but shouldn't
return. The default action is to print a message and abort. */
extern void (*obstack_alloc_failed_handler) (void);
/* Exit value used when 'print_and_abort' is used. */
extern int obstack_exit_failure;
/* Pointer to beginning of object being allocated or to be allocated next.
Note that this might not be the final address of the object
because a new chunk might be needed to hold the final size. */
#define obstack_base(h) ((void *) (h)->object_base)
/* Size for allocating ordinary chunks. */
#define obstack_chunk_size(h) ((h)->chunk_size)
/* Pointer to next byte not yet allocated in current chunk. */
#define obstack_next_free(h) ((void *) (h)->next_free)
/* Mask specifying low bits that should be clear in address of an object. */
#define obstack_alignment_mask(h) ((h)->alignment_mask)
/* To prevent prototype warnings provide complete argument list. */
#define obstack_init(h) \
_obstack_begin ((h), 0, 0, \
_OBSTACK_CAST (void *(*) (size_t), obstack_chunk_alloc), \
_OBSTACK_CAST (void (*) (void *), obstack_chunk_free))
#define obstack_begin(h, size) \
_obstack_begin ((h), (size), 0, \
_OBSTACK_CAST (void *(*) (size_t), obstack_chunk_alloc), \
_OBSTACK_CAST (void (*) (void *), obstack_chunk_free))
#define obstack_specify_allocation(h, size, alignment, chunkfun, freefun) \
_obstack_begin ((h), (size), (alignment), \
_OBSTACK_CAST (void *(*) (size_t), chunkfun), \
_OBSTACK_CAST (void (*) (void *), freefun))
#define obstack_specify_allocation_with_arg(h, size, alignment, chunkfun, freefun, arg) \
_obstack_begin_1 ((h), (size), (alignment), \
_OBSTACK_CAST (void *(*) (void *, size_t), chunkfun), \
_OBSTACK_CAST (void (*) (void *, void *), freefun), arg)
#define obstack_chunkfun(h, newchunkfun) \
((void) ((h)->chunkfun.extra = (void *(*) (void *, size_t)) (newchunkfun)))
#define obstack_freefun(h, newfreefun) \
((void) ((h)->freefun.extra = (void *(*) (void *, void *)) (newfreefun)))
#define obstack_1grow_fast(h, achar) ((void) (*((h)->next_free)++ = (achar)))
#define obstack_blank_fast(h, n) ((void) ((h)->next_free += (n)))
#define obstack_memory_used(h) _obstack_memory_used (h)
#if defined __GNUC__
# if !defined __GNUC_MINOR__ || __GNUC__ * 1000 + __GNUC_MINOR__ < 2008
# define __extension__
# endif
/* For GNU C, if not -traditional,
we can define these macros to compute all args only once
without using a global variable.
Also, we can avoid using the 'temp' slot, to make faster code. */
# define obstack_object_size(OBSTACK) \
__extension__ \
({ struct obstack const *__o = (OBSTACK); \
(_OBSTACK_SIZE_T) (__o->next_free - __o->object_base); })
/* The local variable is named __o1 to avoid a shadowed variable
warning when invoked from other obstack macros. */
# define obstack_room(OBSTACK) \
__extension__ \
({ struct obstack const *__o1 = (OBSTACK); \
(_OBSTACK_SIZE_T) (__o1->chunk_limit - __o1->next_free); })
# define obstack_make_room(OBSTACK, length) \
__extension__ \
({ struct obstack *__o = (OBSTACK); \
_OBSTACK_SIZE_T __len = (length); \
if (obstack_room (__o) < __len) \
_obstack_newchunk (__o, __len); \
(void) 0; })
# define obstack_empty_p(OBSTACK) \
__extension__ \
({ struct obstack const *__o = (OBSTACK); \
(__o->chunk->prev == 0 \
&& __o->next_free == __PTR_ALIGN ((char *) __o->chunk, \
__o->chunk->contents, \
__o->alignment_mask)); })
# define obstack_grow(OBSTACK, where, length) \
__extension__ \
({ struct obstack *__o = (OBSTACK); \
_OBSTACK_SIZE_T __len = (length); \
if (obstack_room (__o) < __len) \
_obstack_newchunk (__o, __len); \
memcpy (__o->next_free, where, __len); \
__o->next_free += __len; \
(void) 0; })
# define obstack_grow0(OBSTACK, where, length) \
__extension__ \
({ struct obstack *__o = (OBSTACK); \
_OBSTACK_SIZE_T __len = (length); \
if (obstack_room (__o) < __len + 1) \
_obstack_newchunk (__o, __len + 1); \
memcpy (__o->next_free, where, __len); \
__o->next_free += __len; \
*(__o->next_free)++ = 0; \
(void) 0; })
# define obstack_1grow(OBSTACK, datum) \
__extension__ \
({ struct obstack *__o = (OBSTACK); \
if (obstack_room (__o) < 1) \
_obstack_newchunk (__o, 1); \
obstack_1grow_fast (__o, datum); })
/* These assume that the obstack alignment is good enough for pointers
or ints, and that the data added so far to the current object
shares that much alignment. */
# define obstack_ptr_grow(OBSTACK, datum) \
__extension__ \
({ struct obstack *__o = (OBSTACK); \
if (obstack_room (__o) < sizeof (void *)) \
_obstack_newchunk (__o, sizeof (void *)); \
obstack_ptr_grow_fast (__o, datum); })
# define obstack_int_grow(OBSTACK, datum) \
__extension__ \
({ struct obstack *__o = (OBSTACK); \
if (obstack_room (__o) < sizeof (int)) \
_obstack_newchunk (__o, sizeof (int)); \
obstack_int_grow_fast (__o, datum); })
# define obstack_ptr_grow_fast(OBSTACK, aptr) \
__extension__ \
({ struct obstack *__o1 = (OBSTACK); \
void *__p1 = __o1->next_free; \
*(const void **) __p1 = (aptr); \
__o1->next_free += sizeof (const void *); \
(void) 0; })
# define obstack_int_grow_fast(OBSTACK, aint) \
__extension__ \
({ struct obstack *__o1 = (OBSTACK); \
void *__p1 = __o1->next_free; \
*(int *) __p1 = (aint); \
__o1->next_free += sizeof (int); \
(void) 0; })
# define obstack_blank(OBSTACK, length) \
__extension__ \
({ struct obstack *__o = (OBSTACK); \
_OBSTACK_SIZE_T __len = (length); \
if (obstack_room (__o) < __len) \
_obstack_newchunk (__o, __len); \
obstack_blank_fast (__o, __len); })
# define obstack_alloc(OBSTACK, length) \
__extension__ \
({ struct obstack *__h = (OBSTACK); \
obstack_blank (__h, (length)); \
obstack_finish (__h); })
# define obstack_copy(OBSTACK, where, length) \
__extension__ \
({ struct obstack *__h = (OBSTACK); \
obstack_grow (__h, (where), (length)); \
obstack_finish (__h); })
# define obstack_copy0(OBSTACK, where, length) \
__extension__ \
({ struct obstack *__h = (OBSTACK); \
obstack_grow0 (__h, (where), (length)); \
obstack_finish (__h); })
/* The local variable is named __o1 to avoid a shadowed variable
warning when invoked from other obstack macros, typically obstack_free. */
# define obstack_finish(OBSTACK) \
__extension__ \
({ struct obstack *__o1 = (OBSTACK); \
void *__value = (void *) __o1->object_base; \
if (__o1->next_free == __value) \
__o1->maybe_empty_object = 1; \
__o1->next_free \
= __PTR_ALIGN (__o1->object_base, __o1->next_free, \
__o1->alignment_mask); \
if ((size_t) (__o1->next_free - (char *) __o1->chunk) \
> (size_t) (__o1->chunk_limit - (char *) __o1->chunk)) \
__o1->next_free = __o1->chunk_limit; \
__o1->object_base = __o1->next_free; \
__value; })
# define obstack_free(OBSTACK, OBJ) \
__extension__ \
({ struct obstack *__o = (OBSTACK); \
void *__obj = (void *) (OBJ); \
if (__obj > (void *) __o->chunk && __obj < (void *) __o->chunk_limit) \
__o->next_free = __o->object_base = (char *) __obj; \
else \
_obstack_free (__o, __obj); })
#else /* not __GNUC__ */
# define obstack_object_size(h) \
((_OBSTACK_SIZE_T) ((h)->next_free - (h)->object_base))
# define obstack_room(h) \
((_OBSTACK_SIZE_T) ((h)->chunk_limit - (h)->next_free))
# define obstack_empty_p(h) \
((h)->chunk->prev == 0 \
&& (h)->next_free == __PTR_ALIGN ((char *) (h)->chunk, \
(h)->chunk->contents, \
(h)->alignment_mask))
/* Note that the call to _obstack_newchunk is enclosed in (..., 0)
so that we can avoid having void expressions
in the arms of the conditional expression.
Casting the third operand to void was tried before,
but some compilers won't accept it. */
# define obstack_make_room(h, length) \
((h)->temp.i = (length), \
((obstack_room (h) < (h)->temp.i) \
? (_obstack_newchunk (h, (h)->temp.i), 0) : 0), \
(void) 0)
# define obstack_grow(h, where, length) \
((h)->temp.i = (length), \
((obstack_room (h) < (h)->temp.i) \
? (_obstack_newchunk ((h), (h)->temp.i), 0) : 0), \
memcpy ((h)->next_free, where, (h)->temp.i), \
(h)->next_free += (h)->temp.i, \
(void) 0)
# define obstack_grow0(h, where, length) \
((h)->temp.i = (length), \
((obstack_room (h) < (h)->temp.i + 1) \
? (_obstack_newchunk ((h), (h)->temp.i + 1), 0) : 0), \
memcpy ((h)->next_free, where, (h)->temp.i), \
(h)->next_free += (h)->temp.i, \
*((h)->next_free)++ = 0, \
(void) 0)
# define obstack_1grow(h, datum) \
(((obstack_room (h) < 1) \
? (_obstack_newchunk ((h), 1), 0) : 0), \
obstack_1grow_fast (h, datum))
# define obstack_ptr_grow(h, datum) \
(((obstack_room (h) < sizeof (char *)) \
? (_obstack_newchunk ((h), sizeof (char *)), 0) : 0), \
obstack_ptr_grow_fast (h, datum))
# define obstack_int_grow(h, datum) \
(((obstack_room (h) < sizeof (int)) \
? (_obstack_newchunk ((h), sizeof (int)), 0) : 0), \
obstack_int_grow_fast (h, datum))
# define obstack_ptr_grow_fast(h, aptr) \
(((const void **) ((h)->next_free += sizeof (void *)))[-1] = (aptr), \
(void) 0)
# define obstack_int_grow_fast(h, aint) \
(((int *) ((h)->next_free += sizeof (int)))[-1] = (aint), \
(void) 0)
# define obstack_blank(h, length) \
((h)->temp.i = (length), \
((obstack_room (h) < (h)->temp.i) \
? (_obstack_newchunk ((h), (h)->temp.i), 0) : 0), \
obstack_blank_fast (h, (h)->temp.i))
# define obstack_alloc(h, length) \
(obstack_blank ((h), (length)), obstack_finish ((h)))
# define obstack_copy(h, where, length) \
(obstack_grow ((h), (where), (length)), obstack_finish ((h)))
# define obstack_copy0(h, where, length) \
(obstack_grow0 ((h), (where), (length)), obstack_finish ((h)))
# define obstack_finish(h) \
(((h)->next_free == (h)->object_base \
? (((h)->maybe_empty_object = 1), 0) \
: 0), \
(h)->temp.p = (h)->object_base, \
(h)->next_free \
= __PTR_ALIGN ((h)->object_base, (h)->next_free, \
(h)->alignment_mask), \
(((size_t) ((h)->next_free - (char *) (h)->chunk) \
> (size_t) ((h)->chunk_limit - (char *) (h)->chunk)) \
? ((h)->next_free = (h)->chunk_limit) : 0), \
(h)->object_base = (h)->next_free, \
(h)->temp.p)
# define obstack_free(h, obj) \
((h)->temp.p = (void *) (obj), \
(((h)->temp.p > (void *) (h)->chunk \
&& (h)->temp.p < (void *) (h)->chunk_limit) \
? (void) ((h)->next_free = (h)->object_base = (char *) (h)->temp.p) \
: _obstack_free ((h), (h)->temp.p)))
#endif /* not __GNUC__ */
#ifdef __cplusplus
} /* C++ */
#endif
#endif /* _OBSTACK_H */

11
pam/crond Normal file
View File

@ -0,0 +1,11 @@
#
# The PAM configuration file for the cron daemon
#
#
# Although no PAM authentication is called, auth modules
# are used for credential setting
auth include system-auth
account required pam_access.so
account include system-auth
session required pam_loginuid.so
session include system-auth

118
src/Makemodule.am Normal file
View File

@ -0,0 +1,118 @@
# Makefile.am - two binaries crond and crontab
sbin_PROGRAMS += \
src/crond
bin_PROGRAMS += \
src/cronnext \
src/crontab
src_crond_SOURCES = \
src/cron.c \
src/database.c \
src/do_command.c \
src/job.c \
src/popen.c \
src/security.c \
src/user.c \
cronie_common.c \
$(common_src)
src_crontab_SOURCES = \
src/crontab.c \
src/security.c \
$(common_src)
src_cronnext_SOURCES = \
src/cronnext.c \
src/database.c \
src/job.c \
src/user.c \
$(common_src)
common_src = \
src/bitstring.h \
src/entry.c \
src/env.c \
src/externs.h \
src/funcs.h \
src/globals.h \
src/macros.h \
src/misc.c \
src/pathnames.h \
src/pw_dup.c \
src/structs.h
common_nodist += cron-paths.h
nodist_src_crond_SOURCES = $(common_nodist)
nodist_src_crontab_SOURCES = $(common_nodist)
nodist_src_cronnext_SOURCES = $(common_nodist)
BUILT_SOURCES += $(common_nodist)
src_crond_LDADD = $(LIBSELINUX) $(LIBPAM) $(LIBAUDIT)
src_crontab_LDADD = $(LIBSELINUX) $(LIBPAM) $(LIBAUDIT)
## if DEBUG
## noinst_PROGRAMS = debug
## endif
# This header contains all the paths.
# If they are configurable, they are declared in configure script.
# Depends on this Makefile, because it uses make variables.
# CCD 2010/09/10 added CRON_HOSTNAME for clustered-cron.
CLEANFILES += cron-paths.h
if HAS_RUNSTATE
cronpidcomment=/* directory of cron pid file */
cronpiddir=\#define CRON_PID_DIR "$(runstatedir)"
else
cronpidcomment=
cronpiddir=
endif
cron-paths.h: Makefile
@echo 'creating $@'
@sed >$@ 's/ *\\$$//' <<\END #\
/* This file has been automatically generated. Do not edit. */ \
\
#ifndef _CRON_PATHS_H_ \
#define _CRON_PATHS_H_ \
\
/* SPOOLDIR is where the crontabs live. \
* This directory will have its modtime updated \
* whenever crontab(1) changes a crontab; this is \
* the signal for cron(8) to look at each individual \
* crontab file and reload those whose modtimes are \
* newer than they were last time around (or which \
* didn't exist last time around...) \
* or it will be checked by inotify \
*/ \
#define SPOOL_DIR "$(SPOOL_DIR)" \
\
/* CRON_HOSTNAME is file in SPOOL_DIR which, if it \
* exists, and does not just contain a line matching \
* the name returned by gethostname(), causes all \
* crontabs in SPOOL_DIR to be ignored. This is \
* intended to be used when clustering hosts sharing \
* one NFS-mounted SPOOL_DIR, and where only one host \
* should use the crontab files here at any one time. \
*/ \
#define CRON_HOSTNAME ".cron.hostname" \
\
/* cron allow/deny file. At least cron.deny must \
* exist for ordinary users to run crontab. \
*/ \
#define CRON_ALLOW "$(sysconfdir)/cron.allow" \
#define CRON_DENY "$(sysconfdir)/cron.deny" \
\
$(cronpidcomment) \
$(cronpiddir) \
\
/* 4.3BSD-style crontab f.e. /etc/crontab */ \
#define SYSCRONTAB "$(SYSCRONTAB)" \
\
/* system crontab dir f.e. /etc/cron.d/ */ \
#define SYS_CROND_DIR "$(SYS_CROND_DIR)" \
\
#define SYSCONFDIR "$(sysconfdir)" \
\
#endif /* _CRON_PATHS_H_ */ \
END

141
src/bitstring.h Normal file
View File

@ -0,0 +1,141 @@
/* $NetBSD: bitstring.h,v 1.3 2003/08/07 11:17:08 agc Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Paul Vixie.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)bitstring.h 8.1 (Berkeley) 7/19/93
*/
#ifndef _BITSTRING_H_
#define _BITSTRING_H_
typedef unsigned char bitstr_t;
/* internal macros */
/* byte of the bitstring bit is in */
#define _bit_byte(bit) \
((bit) >> 3)
/* mask for the bit within its byte */
#define _bit_mask(bit) \
(1 << ((bit)&0x7))
/* external macros */
/* bytes in a bitstring of nbits bits */
#define bitstr_size(nbits) \
((((nbits) - 1) >> 3) + 1)
/* allocate a bitstring */
#define bit_alloc(nbits) \
(bitstr_t *)calloc(1, \
(unsigned int)bitstr_size(nbits) * sizeof(bitstr_t))
/* allocate a bitstring on the stack */
#define bit_decl(name, nbits) \
(name)[bitstr_size(nbits)]
/* is bit N of bitstring name set? */
#define bit_test(name, bit) \
((name)[_bit_byte(bit)] & _bit_mask(bit))
/* set bit N of bitstring name */
#define bit_set(name, bit) \
(name)[_bit_byte(bit)] |= (bitstr_t)_bit_mask(bit)
/* clear bit N of bitstring name */
#define bit_clear(name, bit) \
(name)[_bit_byte(bit)] &= (bitstr_t)~_bit_mask(bit)
/* clear bits start ... stop in bitstring */
#define bit_nclear(name, start, stop) { \
register bitstr_t *_name = name; \
register int _start = start, _stop = stop; \
register int _startbyte = _bit_byte(_start); \
register int _stopbyte = _bit_byte(_stop); \
if (_startbyte == _stopbyte) { \
_name[_startbyte] &= (bitstr_t)((0xff >> (8 - (_start&0x7))) | \
(0xff << ((_stop&0x7) + 1))); \
} else { \
_name[_startbyte] &= (bitstr_t)(0xff >> (8 - (_start&0x7))); \
while (++_startbyte < _stopbyte) \
_name[_startbyte] = 0; \
_name[_stopbyte] &= (bitstr_t)(0xff << ((_stop&0x7) + 1)); \
} \
}
/* set bits start ... stop in bitstring */
#define bit_nset(name, start, stop) { \
register bitstr_t *_name = name; \
register int _start = start, _stop = stop; \
register int _startbyte = _bit_byte(_start); \
register int _stopbyte = _bit_byte(_stop); \
if (_startbyte == _stopbyte) { \
_name[_startbyte] |= (bitstr_t)((0xff << (_start&0x7)) & \
(0xff >> (7 - (_stop&0x7)))); \
} else { \
_name[_startbyte] |= (bitstr_t)(0xff << ((_start)&0x7)); \
while (++_startbyte < _stopbyte) \
_name[_startbyte] = 0xff; \
_name[_stopbyte] |= (bitstr_t)(0xff >> (7 - (_stop&0x7))); \
} \
}
/* find first bit clear in name */
#define bit_ffc(name, nbits, value) { \
register bitstr_t *_name = name; \
register int _byte, _nbits = nbits; \
register int _stopbyte = _bit_byte(_nbits), _value = -1; \
for (_byte = 0; _byte <= _stopbyte; ++_byte) \
if (_name[_byte] != 0xff) { \
_value = _byte << 3; \
for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \
++_value, _stopbyte >>= 1); \
break; \
} \
*(value) = _value; \
}
/* find first bit set in name */
#define bit_ffs(name, nbits, value) { \
register bitstr_t *_name = name; \
register int _byte, _nbits = nbits; \
register int _stopbyte = _bit_byte(_nbits), _value = -1; \
for (_byte = 0; _byte <= _stopbyte; ++_byte) \
if (_name[_byte]) { \
_value = _byte << 3; \
for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \
++_value, _stopbyte >>= 1); \
break; \
} \
*(value) = _value; \
}
#endif /* !_BITSTRING_H_ */

731
src/cron.c Normal file
View File

@ -0,0 +1,731 @@
/* Copyright 1988,1990,1993,1994 by Paul Vixie
* All rights reserved
*/
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
*
* 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.
*/
/*
* Modified 2010/09/12 by Colin Dean, Durham University IT Service,
* to add clustering support.
*/
#include "config.h"
#define MAIN_PROGRAM
#include <errno.h>
#include <langinfo.h>
#include <locale.h>
#include <pwd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/time.h>
#include <fcntl.h>
#ifdef WITH_INOTIFY
# include <sys/inotify.h>
#endif
#include "cronie_common.h"
#include "funcs.h"
#include "globals.h"
#include "pathnames.h"
#if defined WITH_INOTIFY
int inotify_enabled;
#else
# define inotify_enabled 0
#endif
enum timejump { negative, small, medium, large };
static void usage(void) ATTRIBUTE_NORETURN,
run_reboot_jobs(cron_db *),
find_jobs(int, cron_db *, int, int, long),
set_time(int),
cron_sleep(int, cron_db *),
sigchld_handler(int),
sighup_handler(int ATTRIBUTE_UNUSED),
sigchld_reaper(void),
sigintterm_handler(int ATTRIBUTE_UNUSED), parse_args(int c, char *v[]);
static volatile sig_atomic_t got_sighup, got_sigchld, got_sigintterm;
static int timeRunning, virtualTime, clockTime;
static long GMToff;
static int DisableInotify;
#if defined WITH_INOTIFY
/*
* Note that inotify isn't safe to use with clustering, as changes made
* to a shared filesystem on one system cannot be relied on to be notified
* on another system, so use of inotify is disabled at runtime if run with
* clustering enabled.
*/
# if defined ENABLE_SYSCRONTAB
# define NUM_WATCHES 3
int wd[NUM_WATCHES];
const char *watchpaths[NUM_WATCHES] = {SPOOL_DIR, SYS_CROND_DIR, SYSCRONTAB};
# else
# define NUM_WATCHES 2
int wd[NUM_WATCHES];
const char *watchpaths[NUM_WATCHES] = {SPOOL_DIR, SYS_CROND_DIR};
# endif
static void reset_watches(void) {
size_t i;
for (i = 0; i < sizeof (wd) / sizeof (wd[0]); ++i) {
wd[i] = -2;
}
}
void set_cron_unwatched(int fd) {
size_t i;
for (i = 0; i < sizeof (wd) / sizeof (wd[0]); ++i) {
if (wd[i] > 0) {
inotify_rm_watch(fd, wd[i]);
wd[i] = -1;
}
}
}
void set_cron_watched(int fd) {
pid_t pid = getpid();
size_t i;
if (fd < 0) {
inotify_enabled = 0;
return;
}
for (i = 0; i < sizeof (wd) / sizeof (wd[0]); ++i) {
int w;
w = inotify_add_watch(fd, watchpaths[i],
IN_CREATE | IN_CLOSE_WRITE | IN_ATTRIB | IN_MODIFY | IN_MOVED_TO |
IN_MOVED_FROM | IN_MOVE_SELF | IN_DELETE | IN_DELETE_SELF);
if (w < 0 && errno != ENOENT) {
if (wd[i] != -1) {
log_it("CRON", pid, "This directory or file can't be watched",
watchpaths[i], errno);
log_it("CRON", pid, "INFO", "running without inotify support",
0);
}
inotify_enabled = 0;
set_cron_unwatched(fd);
return;
}
wd[i] = w;
}
if (!inotify_enabled) {
log_it("CRON", pid, "INFO", "running with inotify support", 0);
}
inotify_enabled = 1;
}
#endif
static void handle_signals(cron_db * database) {
if (got_sighup) {
got_sighup = 0;
#if defined WITH_INOTIFY
/* watches must be reinstated on reload */
if (inotify_enabled && (EnableClustering != 1)) {
set_cron_unwatched(database->ifd);
reset_watches();
inotify_enabled = 0;
}
#endif
database->mtime = (time_t) 0;
log_close();
}
if (got_sigchld) {
got_sigchld = 0;
sigchld_reaper();
}
}
static void usage(void) {
const char **dflags;
fprintf(stderr, "Usage:\n");
fprintf(stderr, " %s [options]\n", ProgramName);
fprintf(stderr, "\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " -h print this message \n");
fprintf(stderr, " -i deamon runs without inotify support\n");
fprintf(stderr, " -m <comm> off, or specify preferred client for sending mails\n");
fprintf(stderr, " -n run in foreground\n");
fprintf(stderr, " -f run in foreground, the same as -n\n");
fprintf(stderr, " -p permit any crontab\n");
fprintf(stderr, " -P inherit PATH from environment instead of using default value");
fprintf(stderr, " of \"%s\"\n", _PATH_DEFPATH);
fprintf(stderr, " -c enable clustering support\n");
fprintf(stderr, " -s log into syslog instead of sending mails\n");
fprintf(stderr, " -V print version and exit\n");
fprintf(stderr, " -x <flag> print debug information\n");
fprintf(stderr, "\n");
fprintf(stderr, "Debugging flags are: ");
for (dflags = DebugFlagNames; *dflags; dflags++)
fprintf(stderr, "%s%s", *dflags, dflags[1] ? "," : "\n");
exit(ERROR_EXIT);
}
int main(int argc, char *argv[]) {
struct sigaction sact;
cron_db database;
int fd;
char *cs;
pid_t pid = getpid();
long oldGMToff;
struct timeval tv;
struct timezone tz;
char buf[256];
if ((ProgramName=strrchr(argv[0], '/')) == NULL) {
ProgramName = argv[0];
}
else {
++ProgramName;
}
MailCmd[0] = '\0';
cron_default_mail_charset[0] = '\0';
setlocale(LC_ALL, "");
#if defined(BSD)
setlinebuf(stdout);
setlinebuf(stderr);
#endif
SyslogOutput = 0;
NoFork = 0;
ChangePath = 1;
parse_args(argc, argv);
memset((char *) &sact, 0, sizeof sact);
sigemptyset(&sact.sa_mask);
sact.sa_flags = 0;
#ifdef SA_RESTART
sact.sa_flags |= SA_RESTART;
#endif
sact.sa_handler = sigchld_handler;
(void) sigaction(SIGCHLD, &sact, NULL);
sact.sa_handler = sighup_handler;
(void) sigaction(SIGHUP, &sact, NULL);
sact.sa_handler = sigintterm_handler;
(void) sigaction(SIGINT, &sact, NULL);
(void) sigaction(SIGTERM, &sact, NULL);
acquire_daemonlock(0);
set_cron_uid();
check_spool_dir();
if (ChangePath) {
if (setenv("PATH", _PATH_DEFPATH, 1) < 0) {
log_it("CRON", pid, "DEATH", "can't setenv PATH",
errno);
exit(1);
}
}
/* Get the default locale character set for the mail
* "Content-Type: ...; charset=" header
*/
setlocale(LC_ALL, ""); /* set locale to system defaults or to
* that specified by any LC_* env vars */
if ((cs = nl_langinfo(CODESET)) != NULL)
strncpy(cron_default_mail_charset, cs, MAX_ENVSTR-1);
else
strcpy(cron_default_mail_charset, "US-ASCII");
/* if there are no debug flags turned on, fork as a daemon should.
*/
if (DebugFlags) {
#if DEBUGGING
(void) fprintf(stderr, "[%ld] cron started\n", (long) getpid());
#endif
}
else if (NoFork == 0) {
switch (fork()) {
case -1:
log_it("CRON", pid, "DEATH", "can't fork", errno);
exit(0);
break;
case 0:
/* child process */
(void) setsid();
if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) >= 0) {
(void) dup2(fd, STDIN);
(void) dup2(fd, STDOUT);
(void) dup2(fd, STDERR);
if (fd != STDERR)
(void) close(fd);
}
break;
default:
/* parent process should just die */
_exit(0);
}
}
log_it("CRON", getpid(), "STARTUP", PACKAGE_VERSION, 0);
if (!SyslogOutput && MailCmd[0] == '\0' && access("/usr/sbin/sendmail", X_OK) != 0) {
SyslogOutput=1;
log_it("CRON", pid, "INFO","Syslog will be used instead of sendmail.", 0);
}
pid = getpid();
/* obtain a random scaling factor for RANDOM_DELAY */
if (gettimeofday(&tv, &tz) != 0)
tv.tv_usec = 0;
srandom((unsigned int)(pid + tv.tv_usec));
RandomScale = (double)random() / (double)(1lu << 31);
snprintf(buf, sizeof(buf), "RANDOM_DELAY will be scaled with factor %d%% if used.", (int)(RandomScale*100));
log_it("CRON", pid, "INFO", buf, 0);
acquire_daemonlock(0);
fd = -1;
#if defined WITH_INOTIFY
if (DisableInotify || EnableClustering) {
log_it("CRON", getpid(), "No inotify - daemon runs with -i or -c option",
"", 0);
}
else {
reset_watches();
database.ifd = fd = inotify_init();
fcntl(fd, F_SETFD, FD_CLOEXEC);
if (fd < 0)
log_it("CRON", pid, "INFO", "Inotify init failed", errno);
set_cron_watched(fd);
}
#endif
database.head = NULL;
database.tail = NULL;
database.mtime = (time_t) 0;
load_database(&database);
set_time(TRUE);
run_reboot_jobs(&database);
timeRunning = virtualTime = clockTime;
oldGMToff = GMToff;
/*
* Too many clocks, not enough time (Al. Einstein)
* These clocks are in minutes since the epoch, adjusted for timezone.
* virtualTime: is the time it *would* be if we woke up
* promptly and nobody ever changed the clock. It is
* monotonically increasing... unless a timejump happens.
* At the top of the loop, all jobs for 'virtualTime' have run.
* timeRunning: is the time we last awakened.
* clockTime: is the time when set_time was last called.
*/
while (!got_sigintterm) {
int timeDiff;
enum timejump wakeupKind;
/* ... wait for the time (in minutes) to change ... */
do {
cron_sleep(timeRunning + 1, &database);
set_time(FALSE);
} while (!got_sigintterm && clockTime == timeRunning);
if (got_sigintterm)
break;
timeRunning = clockTime;
/*
* Calculate how the current time differs from our virtual
* clock. Classify the change into one of 4 cases.
*/
timeDiff = timeRunning - virtualTime;
check_orphans(&database);
#if defined WITH_INOTIFY
if (inotify_enabled) {
check_inotify_database(&database);
}
else {
if (load_database(&database) && (EnableClustering != 1))
/* try reinstating the watches */
set_cron_watched(fd);
}
#else
load_database(&database);
#endif
/* shortcut for the most common case */
if (timeDiff == 1) {
virtualTime = timeRunning;
oldGMToff = GMToff;
find_jobs(virtualTime, &database, TRUE, TRUE, oldGMToff);
}
else {
if (timeDiff > (3 * MINUTE_COUNT) || timeDiff < -(3 * MINUTE_COUNT))
wakeupKind = large;
else if (timeDiff > 5)
wakeupKind = medium;
else if (timeDiff > 0)
wakeupKind = small;
else
wakeupKind = negative;
switch (wakeupKind) {
case small:
/*
* case 1: timeDiff is a small positive number
* (wokeup late) run jobs for each virtual
* minute until caught up.
*/
Debug(DSCH, ("[%ld], normal case %d minutes to go\n",
(long) pid, timeDiff));
do {
if (job_runqueue())
sleep(10);
virtualTime++;
if (virtualTime >= timeRunning)
/* always run also the other timezone jobs in the last step */
oldGMToff = GMToff;
find_jobs(virtualTime, &database, TRUE, TRUE, oldGMToff);
} while (virtualTime < timeRunning);
break;
case medium:
/*
* case 2: timeDiff is a medium-sized positive
* number, for example because we went to DST
* run wildcard jobs once, then run any
* fixed-time jobs that would otherwise be
* skipped if we use up our minute (possible,
* if there are a lot of jobs to run) go
* around the loop again so that wildcard jobs
* have a chance to run, and we do our
* housekeeping.
*/
Debug(DSCH, ("[%ld], DST begins %d minutes to go\n",
(long) pid, timeDiff));
/* run wildcard jobs for current minute */
find_jobs(timeRunning, &database, TRUE, FALSE, GMToff);
/* run fixed-time jobs for each minute missed */
do {
if (job_runqueue())
sleep(10);
virtualTime++;
if (virtualTime >= timeRunning)
/* always run also the other timezone jobs in the last step */
oldGMToff = GMToff;
find_jobs(virtualTime, &database, FALSE, TRUE, oldGMToff);
set_time(FALSE);
} while (virtualTime < timeRunning && clockTime == timeRunning);
break;
case negative:
/*
* case 3: timeDiff is a small or medium-sized
* negative num, eg. because of DST ending.
* Just run the wildcard jobs. The fixed-time
* jobs probably have already run, and should
* not be repeated. Virtual time does not
* change until we are caught up.
*/
Debug(DSCH, ("[%ld], DST ends %d minutes to go\n",
(long) pid, timeDiff));
find_jobs(timeRunning, &database, TRUE, FALSE, GMToff);
break;
default:
/*
* other: time has changed a *lot*,
* jump virtual time, and run everything
*/
Debug(DSCH, ("[%ld], clock jumped\n", (long) pid));
virtualTime = timeRunning;
oldGMToff = GMToff;
find_jobs(timeRunning, &database, TRUE, TRUE, GMToff);
}
}
/* Jobs to be run (if any) are loaded; clear the queue. */
job_runqueue();
handle_signals(&database);
}
#if defined WITH_INOTIFY
if (inotify_enabled && (EnableClustering != 1))
set_cron_unwatched(fd);
if (fd >= 0 && close(fd) < 0)
log_it("CRON", pid, "INFO", "Inotify close failed", errno);
#endif
log_it("CRON", pid, "INFO", "Shutting down", 0);
(void) unlink(_PATH_CRON_PID);
return 0;
}
static void run_reboot_jobs(cron_db * db) {
user *u;
entry *e;
int reboot;
pid_t pid = getpid();
/* lock exist - skip reboot jobs */
if (access(REBOOT_LOCK, F_OK) == 0) {
log_it("CRON", pid, "INFO",
"@reboot jobs will be run at computer's startup.", 0);
return;
}
/* lock doesn't exist - create lock, run reboot jobs */
if ((reboot = creat(REBOOT_LOCK, S_IRUSR & S_IWUSR)) < 0)
log_it("CRON", pid, "INFO", "Can't create lock for reboot jobs.",
errno);
else
close(reboot);
for (u = db->head; u != NULL; u = u->next) {
for (e = u->crontab; e != NULL; e = e->next) {
if (e->flags & WHEN_REBOOT)
job_add(e, u);
}
}
(void) job_runqueue();
}
static void find_jobs(int vtime, cron_db * db, int doWild, int doNonWild, long vGMToff) {
char *orig_tz, *job_tz;
struct tm *tm;
int minute, hour, dom, month, dow;
user *u;
entry *e;
/* The support for the job-specific timezones is not perfect. There will
* be jobs missed or run twice during the DST change in the job timezone.
* It is recommended not to schedule any jobs during the hour when
* the DST changes happen if job-specific timezones are used.
*
* Make 0-based values out of tm values so we can use them as indicies
*/
#define maketime(tz1, tz2) do { \
char *t = tz1; \
if (t != NULL && *t != '\0') { \
setenv("TZ", t, 1); \
tm = localtime(&virtualGMTSecond); \
} else { if ((tz2) != NULL) \
setenv("TZ", (tz2), 1); \
else \
unsetenv("TZ"); \
tm = gmtime(&virtualSecond); \
} \
minute = tm->tm_min -FIRST_MINUTE; \
hour = tm->tm_hour -FIRST_HOUR; \
dom = tm->tm_mday -FIRST_DOM; \
month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH; \
dow = tm->tm_wday -FIRST_DOW; \
} while (0)
orig_tz = getenv("TZ");
/* 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'. yes, it's bizarre.
* like many bizarre things, it's the standard.
*/
for (u = db->head; u != NULL; u = u->next) {
for (e = u->crontab; e != NULL; e = e->next) {
time_t virtualSecond = (time_t)(vtime - e->delay) * (time_t)SECONDS_PER_MINUTE;
time_t virtualGMTSecond = virtualSecond - vGMToff;
job_tz = env_get("CRON_TZ", e->envp);
maketime(job_tz, orig_tz);
/* here we test whether time is NOW */
if (bit_test(e->minute, minute) &&
bit_test(e->hour, hour) &&
bit_test(e->month, month) &&
(((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
? (bit_test(e->dow, dow) && bit_test(e->dom, dom))
: (bit_test(e->dow, dow) || bit_test(e->dom, dom))
)
) {
if (job_tz != NULL && vGMToff != GMToff)
/* do not try to run the jobs from different timezones
* during the DST switch of the default timezone.
*/
continue;
if ((doNonWild &&
!(e->flags & (MIN_STAR | HR_STAR))) ||
(doWild && (e->flags & (MIN_STAR | HR_STAR))))
job_add(e, u); /*will add job, if it isn't in queue already for NOW. */
}
}
}
if (orig_tz != NULL)
setenv("TZ", orig_tz, 1);
else
unsetenv("TZ");
}
/*
* Set StartTime and clockTime to the current time.
* These are used for computing what time it really is right now.
* Note that clockTime is a unix wallclock time converted to minutes.
*/
static void set_time(int initialize) {
struct tm tm;
static int isdst;
StartTime = time(NULL);
/* We adjust the time to GMT so we can catch DST changes. */
tm = *localtime(&StartTime);
if (initialize || tm.tm_isdst != isdst) {
isdst = tm.tm_isdst;
GMToff = get_gmtoff(&StartTime, &tm);
Debug(DSCH, ("[%ld] GMToff=%ld\n", (long) getpid(), (long) GMToff));
}
clockTime = (int)((StartTime + GMToff) / (time_t) SECONDS_PER_MINUTE);
}
/*
* Try to just hit the next minute.
*/
static void cron_sleep(int target, cron_db * db) {
time_t t1, t2;
int seconds_to_wait;
t1 = time(NULL) + GMToff;
seconds_to_wait = (int) (target * SECONDS_PER_MINUTE - t1) + 1;
Debug(DSCH, ("[%ld] Target time=%ld, sec-to-wait=%d\n",
(long) getpid(), (long) target * SECONDS_PER_MINUTE,
seconds_to_wait));
while (seconds_to_wait > 0 && seconds_to_wait < 65) {
sleep((unsigned int) seconds_to_wait);
if (got_sigintterm)
return;
/*
* Check to see if we were interrupted by a signal.
* If so, service the signal(s) then continue sleeping
* where we left off.
*/
handle_signals(db);
t2 = time(NULL) + GMToff;
seconds_to_wait -= (int) (t2 - t1);
t1 = t2;
}
}
static void sighup_handler(int x ATTRIBUTE_UNUSED) {
got_sighup = 1;
}
static void sigchld_handler(int x ATTRIBUTE_UNUSED) {
got_sigchld = 1;
}
static void sigintterm_handler(int x ATTRIBUTE_UNUSED) {
got_sigintterm = 1;
}
static void sigchld_reaper(void) {
WAIT_T waiter;
PID_T pid;
do {
pid = waitpid(-1, &waiter, WNOHANG);
switch (pid) {
case -1:
if (errno == EINTR)
continue;
Debug(DPROC, ("[%ld] sigchld...no children\n", (long) getpid()));
break;
case 0:
Debug(DPROC, ("[%ld] sigchld...no dead kids\n", (long) getpid()));
break;
default:
Debug(DPROC,
("[%ld] sigchld...pid #%ld died, stat=%d\n",
(long) getpid(), (long) pid, WEXITSTATUS(waiter)));
break;
}
} while (pid > 0);
}
static void parse_args(int argc, char *argv[]) {
int argch;
while (-1 != (argch = getopt(argc, argv, "hnfpsiPx:m:cV"))) {
switch (argch) {
case 'x':
if (!set_debug_flags(optarg))
usage();
break;
case 'n':
case 'f':
NoFork = 1;
break;
case 'p':
PermitAnyCrontab = 1;
break;
case 's':
SyslogOutput = 1;
break;
case 'i':
DisableInotify = 1;
break;
case 'P':
ChangePath = 0;
break;
case 'm':
strncpy(MailCmd, optarg, MAX_COMMAND);
break;
case 'c':
EnableClustering = 1;
break;
case 'V':
puts(PACKAGE_STRING);
exit(EXIT_SUCCESS);
case 'h':
default:
usage();
break;
}
}
}

435
src/cronnext.c Normal file
View File

@ -0,0 +1,435 @@
/*
cronnext - calculate the time cron will execute the next job
Copyright (C) 2016 Marco Migliori <sgerwk@aol.com>
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.
The GNU General Public License can also be found in the file
`COPYING' that comes with the Anacron source distribution.
*/
#include "config.h"
#define MAIN_PROGRAM
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <pwd.h>
#include "globals.h"
#include "funcs.h"
#include "cron-paths.h"
/* flags to crontab search */
#define ENTRIES 0x01 // print entries
#define CRONTABS 0x02 // print crontabs
#define SYSTEM 0x04 // include system crontab
#define ALLJOBS 0x08 // print all jobs in interval
#ifdef WITH_INOTIFY
void set_cron_watched(int fd) {
/* empty stub */
(void)fd;
}
#endif
void do_command(entry *e, user *u) {
/* empty stub */
(void)e;
(void)u;
}
#ifdef WITH_SELINUX
int get_security_context(const char *name, int crontab_fd,
security_context_t *rcontext, const char *tabname) {
/* empty stub */
(void)name;
(void)crontab_fd;
(void)tabname;
*rcontext = NULL;
return 0;
}
void free_security_context(security_context_t *scontext) {
/* empty stub */
(void)scontext;
}
#endif
/*
* print entry flags
*/
const char *flagname[]= {
"MIN_STAR",
"HR_STAR",
"DOM_STAR",
"DOW_STAR",
"WHEN_REBOOT",
"DONT_LOG"
};
void printflags(char *indent, int flags) {
size_t f;
int first = 1;
printf("%s flagnames:", indent);
for (f = 0; f < sizeof(flagname)/sizeof(char *); f++)
if (flags & (int)1 << f) {
printf("%s%s", first ? " " : "|", flagname[f]);
first = 0;
}
printf("\n");
}
/*
* print a crontab entry
*/
void printentry(char *indent, entry *e, time_t next) {
printf("%s - user: %s\n", indent, e->pwd->pw_name);
printf("%s cmd: \"%s\"\n", indent, e->cmd);
printf("%s flags: 0x%02X\n", indent, e->flags);
printflags(indent, e->flags);
printf("%s delay: %d\n", indent, e->delay);
printf("%s next: %ld\n", indent, (long)next);
printf("%s nextstring: ", indent);
printf("%s", asctime(localtime(&next)));
}
/*
* print a crontab data
*/
void printcrontab(user *u) {
printf(" - user: \"%s\"\n", u->name);
printf(" crontab: %s\n", u->tabname);
printf(" system: %d\n", u->system);
printf(" entries:\n");
}
/*
* basic algorithm: iterate over time from now to 8 year ahead in default steps
* of 1 minute, checking whether time matches a crontab entry at each step (8
* years is the largest interval between two crontab matches)
*
* to save iterations, use larger steps if month or day don't match the entry:
* - if the month doesn't match, skip to 00:00 of the first day of next month
* - for the day, avoid the complication of the different length of months: if
* neither the day nor the next day match, increase time of one day
*/
/*
* check whether time matches day of month and/or day of week; this requires
* checking dom if dow=*, dow if dom=*, either one otherwise; see comment "the
* dom/dow situation is odd..." in cron.c
*/
int matchday(entry *e, time_t time) {
struct tm current;
localtime_r(&time, &current);
if (e->flags & DOW_STAR)
return bit_test(e->dom, current.tm_mday - 1);
if (e->flags & DOM_STAR)
return bit_test(e->dow, current.tm_wday);
return bit_test(e->dom, current.tm_mday - 1) ||
bit_test(e->dow, current.tm_wday);
}
/*
* next time matching a crontab entry
*/
time_t nextmatch(entry *e, time_t start, time_t end) {
time_t time;
struct tm current;
for (time = start; time <= end; ) {
localtime_r(&time, &current);
/* month doesn't match: move to 1st of next month */
if (!bit_test(e->month, current.tm_mon)) {
current.tm_mon++;
if (current.tm_mon >= 12) {
current.tm_year++;
current.tm_mon = 0;
}
current.tm_mday = 1;
current.tm_hour = 0;
current.tm_min = 0;
time = mktime(&current);
continue;
}
/* neither time nor time+1day match day: increase 1 day */
if (!matchday(e, time) && !matchday(e, time + 24 * 60 * 60)) {
time += 24 * 60 * 60;
continue;
}
/* if time matches, return time;
* check for month is redudant, but check for day is
* necessary because we only know that either time
* or time+1day match */
if (bit_test(e->month, current.tm_mon) &&
matchday(e, time) &&
bit_test(e->hour, current.tm_hour) &&
bit_test(e->minute, current.tm_min)
)
return time;
/* skip to next minute */
time += 60;
}
return -1;
}
/*
* match a user against a list
*/
int matchuser(char *user_name, char *list) {
char *pos;
size_t l = strlen(user_name);
for (pos = list; (pos = strstr(pos, user_name)) != NULL; pos += l) {
if ((pos != list) && (*(pos - 1) != ','))
continue;
if ((pos[l] != '\0') && (pos[l] != ','))
continue;
return 1;
}
return 0;
}
/*
* find next sheduled job
*/
time_t cronnext(cron_db database,
time_t start, time_t end,
char *include, char *exclude, char *command, int flags) {
time_t closest, next;
user *u;
entry *e;
char *indent = "";
if (flags & CRONTABS) {
printf("crontabs:\n");
indent = " ";
}
else if (flags & ALLJOBS)
printf("jobs:\n");
/* find next sheduled time */
closest = -1;
for (u = database.head; u; u = u->next) {
if (include && !matchuser(u->name, include))
continue;
if (exclude && matchuser(u->name, exclude))
continue;
if (!(flags & SYSTEM) && u->system)
continue;
if (flags & CRONTABS)
printcrontab(u);
for (e = u->crontab; e; e = e->next) {
if (command && strstr(e->cmd, command) == NULL)
continue;
for (next = nextmatch(e, start, end);
next <= end;
next = nextmatch(e, next + 60, end)) {
if (next < 0)
break;
if (closest < 0 || next < closest)
closest = next;
if (flags & ENTRIES)
printentry(indent, e, next);
if (! (flags & ALLJOBS))
break;
}
}
}
return closest;
}
/*
* load installed crontabs and/or crontab files
*/
cron_db database(int installed, char **additional) {
cron_db db = {NULL, NULL, (time_t) 0};
struct passwd pw;
int fd;
struct stat ss;
user *u;
if (installed)
load_database(&db);
for ( ; *additional != NULL; additional++) {
fd = open(*additional, O_RDONLY);
if (fd == -1) {
perror(*additional);
continue;
}
fstat(fd, &ss);
if (S_ISDIR(ss.st_mode)) {
fprintf(stderr, "%s is a directory - skipping\n", *additional);
close(fd);
continue;
}
memset(&pw, 0, sizeof(pw));
pw.pw_name = *additional;
pw.pw_passwd = "";
pw.pw_dir = ".";
u = load_user(fd, &pw, *additional, *additional, *additional);
if (u == NULL) {
printf("cannot load crontab %s\n", *additional);
continue;
}
link_user(&db, u);
}
return db;
}
void usage() {
fprintf(stderr, "Find the time of the next scheduled cron job.\n");
fprintf(stderr, "Usage:\n");
fprintf(stderr, " cronnext [options] [file ...]\n");
fprintf(stderr, "\n");
fprintf(stderr, "Options:\n");
fprintf(stderr, " -i users include only the crontab of these users\n");
fprintf(stderr, " -e users exclude the crontab of these users\n");
fprintf(stderr, " -s do not include the system crontab\n");
fprintf(stderr, " -a examine installed crontabs even if files are given\n");
fprintf(stderr, " -t time start from this time (seconds since epoch)\n");
fprintf(stderr, " -q time end check at this time (seconds since epoch)\n");
fprintf(stderr, " -j cmd only check jobs that contain cmd as a substring\n");
fprintf(stderr, " -l print next jobs to be executed\n");
fprintf(stderr, " -c print next execution of each job\n");
fprintf(stderr, " -f print all jobs executed in the given interval\n");
fprintf(stderr, " -h this help\n");
fprintf(stderr, " -V print version and exit\n");
}
/*
* main
*/
int main(int argn, char *argv[]) {
int opt;
char *include, *exclude, *command;
int flags;
time_t start, next, end = 0;
int endtime, printjobs;
cron_db db;
int installed = 0;
include = NULL;
exclude = NULL;
command = NULL;
flags = SYSTEM;
endtime = 0;
printjobs = 0;
start = time(NULL) / 60 * 60;
while (-1 != (opt = getopt(argn, argv, "i:e:ast:q:j:lcfhV"))) {
switch (opt) {
case 'i':
include = optarg;
break;
case 'e':
exclude = optarg;
break;
case 'a':
installed = 1;
break;
case 's':
flags &= ~SYSTEM;
break;
case 't':
start = atoi(optarg) / 60 * 60;
break;
case 'q':
end = atoi(optarg) / 60 * 60;
endtime = 1;
break;
case 'j':
command = optarg;
break;
case 'l':
printjobs = 1;
break;
case 'c':
flags |= ENTRIES | CRONTABS;
break;
case 'f':
flags |= ALLJOBS | ENTRIES;
break;
case 'h':
usage();
return EXIT_SUCCESS;
case 'V':
puts(PACKAGE_STRING);
return EXIT_SUCCESS;
default:
fprintf(stderr, "unrecognized option: %s\n",
argv[optind - 1]);
usage();
exit(EXIT_FAILURE);
}
}
if (flags & ALLJOBS && !endtime) {
fprintf(stderr, "no ending time specified: -f requires -q\n");
usage();
exit(EXIT_FAILURE);
}
/* maximum match interval is 8 years:
* crontab has '* * 29 2 *' and we are on 1 March 2096:
* next matching time will be 29 February 2104 */
if (!endtime)
end = start + 8 * 12 * 31 * 24 * 60 * 60;
/* debug cron */
if (flags & CRONTABS) {
printf("spool: %s\n", SPOOL_DIR);
set_debug_flags("");
}
/* "load,pars" for debugging loading and parsing, "" for nothing
see globals.h for symbolic names and macros.h for meaning */
/* load database */
db = database(installed || argv[optind] == NULL, argv + optind);
/* find time of next scheduled command */
next = cronnext(db, start, end, include, exclude, command, flags);
/* print time */
if (next == -1)
return EXIT_FAILURE;
else
printf("next: %ld\n", (long) next);
/* print next jobs */
if (printjobs) {
printf("nextjobs:\n");
cronnext(db, next, next, include, exclude, command, (flags & SYSTEM) | ENTRIES);
}
return EXIT_SUCCESS;
}

1119
src/crontab.c Normal file

File diff suppressed because it is too large Load Diff

682
src/database.c Normal file
View File

@ -0,0 +1,682 @@
/* Copyright 1988,1990,1993,1994 by Paul Vixie
* All rights reserved
*/
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
*
* 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.
*/
/* vix 26jan87 [RCS has the log]
*/
/*
* Modified 2010/09/12 by Colin Dean, Durham University IT Service,
* to add clustering support.
*/
#include "config.h"
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <pwd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef WITH_INOTIFY
# include <sys/inotify.h>
#endif
#include "funcs.h"
#include "globals.h"
#include "pathnames.h"
/* size of the event structure, not counting name */
#define EVENT_SIZE (sizeof (struct inotify_event))
/* reasonable guess as to size of 1024 events */
#define BUF_LEN (1024 * (EVENT_SIZE + 16))
static void overwrite_database(cron_db *, cron_db *);
static void process_crontab(const char *, const char *,
const char *, cron_db *, cron_db *);
static int not_a_crontab(DIR_T * dp);
/* return 1 if we should skip this file */
static void max_mtime(const char *dir_name, struct stat *max_st);
/* record max mtime of any file under dir_name in max_st */
static int
check_open(const char *tabname, const char *uname,
struct passwd *pw, time_t * mtime) {
struct stat statbuf;
int crontab_fd;
pid_t pid = getpid();
if ((crontab_fd =
open(tabname, O_RDONLY | O_NONBLOCK, 0)) == -1) {
log_it(uname, pid, "CAN'T OPEN", tabname, errno);
return (-1);
}
if (fstat(crontab_fd, &statbuf) < OK) {
log_it(uname, pid, "STAT FAILED", tabname, errno);
close(crontab_fd);
return (-1);
}
*mtime = statbuf.st_mtime;
if (PermitAnyCrontab == 0) {
if (!S_ISREG(statbuf.st_mode)) {
log_it(uname, pid, "NOT REGULAR", tabname, 0);
close(crontab_fd);
return (-1);
}
if ((statbuf.st_mode & 07533) != 0400) {
log_it(uname, pid, "BAD FILE MODE", tabname, 0);
close(crontab_fd);
return (-1);
}
if (statbuf.st_uid != ROOT_UID && (pw == NULL ||
statbuf.st_uid != pw->pw_uid ||
strcmp(uname, pw->pw_name) != 0)) {
log_it(uname, pid, "WRONG FILE OWNER", tabname, 0);
close(crontab_fd);
return (-1);
}
if (pw && statbuf.st_nlink != 1) {
log_it(uname, pid, "BAD LINK COUNT", tabname, 0);
close(crontab_fd);
return (-1);
}
}
return (crontab_fd);
}
static orphan *orphans;
static void
free_orphan(orphan *o) {
free(o->tabname);
free(o->fname);
free(o->uname);
free(o);
}
void
check_orphans(cron_db *db) {
orphan *prev_orphan = NULL;
orphan *o = orphans;
while (o != NULL) {
if (getpwnam(o->uname) != NULL) {
orphan *next = o->next;
if (prev_orphan == NULL) {
orphans = next;
} else {
prev_orphan->next = next;
}
process_crontab(o->uname, o->fname, o->tabname,
db, NULL);
/* process_crontab could have added a new orphan */
if (prev_orphan == NULL && orphans != next) {
prev_orphan = orphans;
}
free_orphan(o);
o = next;
} else {
prev_orphan = o;
o = o->next;
}
}
}
static int
find_orphan(const char *uname, const char *fname, const char *tabname) {
orphan *o;
for (o = orphans; o != NULL; o = o->next) {
if (uname && o->uname) {
if (strcmp(uname, o->uname) != 0)
continue;
} else if (uname != o->uname)
continue;
if (fname && o->fname) {
if (strcmp(fname, o->fname) != 0)
continue;
} else if (fname != o->fname)
continue;
if (tabname && o->tabname) {
if (strcmp(tabname, o->tabname) != 0)
continue;
} else if (tabname != o->tabname)
continue;
return 1;
}
return 0;
}
static void
add_orphan(const char *uname, const char *fname, const char *tabname) {
orphan *o;
if (find_orphan(uname, fname, tabname))
return;
o = calloc(1, sizeof(*o));
if (o == NULL)
return;
if (uname)
if ((o->uname=strdup(uname)) == NULL)
goto cleanup;
if (fname)
if ((o->fname=strdup(fname)) == NULL)
goto cleanup;
if (tabname)
if ((o->tabname=strdup(tabname)) == NULL)
goto cleanup;
o->next = orphans;
orphans = o;
return;
cleanup:
free_orphan(o);
}
static void
process_crontab(const char *uname, const char *fname, const char *tabname,
cron_db * new_db, cron_db * old_db) {
struct passwd *pw = NULL;
int crontab_fd = -1;
user *u = NULL;
time_t mtime;
int crond_crontab = (fname == NULL) && (strcmp(tabname, SYSCRONTAB) != 0);
if (fname == NULL) {
/* must be set to something for logging purposes.
*/
fname = "*system*";
}
else if ((pw = getpwnam(uname)) == NULL) {
/* file doesn't have a user in passwd file.
*/
log_it(uname, getpid(), "ORPHAN", "no passwd entry", 0);
add_orphan(uname, fname, tabname);
goto next_crontab;
}
if ((crontab_fd = check_open(tabname, uname, pw, &mtime)) == -1)
goto next_crontab;
mtime = TMIN(new_db->mtime, mtime);
Debug(DLOAD, ("\t%s:", fname));
if (old_db != NULL)
u = find_user(old_db, fname, crond_crontab ? tabname : NULL); /* find user in old_db */
if (u != NULL) {
/* if crontab has not changed since we last read it
* in, then we can just use our existing entry.
*/
if (u->mtime == mtime) {
Debug(DLOAD, (" [no change, using old data]"));
unlink_user(old_db, u);
link_user(new_db, u);
goto next_crontab;
}
/* before we fall through to the code that will reload
* the user, let's deallocate and unlink the user in
* the old database. This is more a point of memory
* efficiency than anything else, since all leftover
* users will be deleted from the old database when
* we finish with the crontab...
*/
Debug(DLOAD, (" [delete old data]"));
unlink_user(old_db, u);
free_user(u);
log_it(fname, getpid(), "RELOAD", tabname, 0);
}
u = load_user(crontab_fd, pw, uname, fname, tabname); /* read the file */
crontab_fd = -1; /* load_user takes care of closing the file */
if (u != NULL) {
u->mtime = mtime;
link_user(new_db, u);
}
next_crontab:
if (crontab_fd != -1) {
Debug(DLOAD, (" [done]\n"));
close(crontab_fd);
}
}
static int
cluster_host_is_local(void)
{
char filename[NAME_MAX+1];
int is_local;
FILE *f;
char hostname[MAXHOSTNAMELEN], myhostname[MAXHOSTNAMELEN];
if (!EnableClustering)
return (1);
/* to allow option of NFS-mounting SPOOL_DIR on a cluster of
* hosts and to only use crontabs here on any one host at a
* time, allow for existence of a CRON_HOSTNAME file, and if
* it doesn't exist, or exists but does not contain this
* host's hostname, then skip the crontabs.
*
* Note: for safety's sake, no CRON_HOSTNAME file means skip,
* otherwise its accidental deletion could result in multiple
* cluster hosts running the same cron jobs, which is
* potentially worse.
*/
is_local = 0;
if (glue_strings(filename, sizeof filename, SPOOL_DIR, CRON_HOSTNAME, '/')) {
if ((f = fopen(filename, "r"))) {
if (EOF != get_string(hostname, MAXHOSTNAMELEN, f, "\n") &&
gethostname(myhostname, MAXHOSTNAMELEN) == 0) {
is_local = (strcmp(myhostname, hostname) == 0);
} else {
Debug(DLOAD, ("cluster: hostname comparison error\n"));
}
fclose(f);
} else {
Debug(DLOAD, ("cluster: file %s not found\n", filename));
}
}
return (is_local);
}
#if defined WITH_INOTIFY
void check_inotify_database(cron_db * old_db) {
cron_db new_db;
DIR_T *dp;
DIR *dir;
struct timeval tv;
fd_set rfds;
int retval;
char buf[BUF_LEN];
pid_t pid = getpid();
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO(&rfds);
FD_SET(old_db->ifd, &rfds);
retval = select(old_db->ifd + 1, &rfds, NULL, NULL, &tv);
if (retval == -1) {
if (errno != EINTR)
log_it("CRON", pid, "INOTIFY", "select failed", errno);
return;
}
else if (FD_ISSET(old_db->ifd, &rfds)) {
new_db.head = new_db.tail = NULL;
new_db.ifd = old_db->ifd;
new_db.mtime = time(NULL) - 1;
while ((retval = (int)read(old_db->ifd, buf, sizeof (buf))) == -1 &&
errno == EINTR) ;
if (retval == 0) {
/* this should not happen as the buffer is large enough */
errno = ENOMEM;
}
if (retval <= 0) {
log_it("CRON", pid, "INOTIFY", "read failed", errno);
/* something fatal must have occurred we have no other reasonable
* way how to handle this failure than exit.
*/
(void) exit(ERROR_EXIT);
}
/* we must reinstate the watches here - TODO reinstate only watches
* which get IN_IGNORED event
*/
set_cron_watched(old_db->ifd);
/* TODO: parse the events and read only affected files */
#if defined ENABLE_SYSCRONTAB
process_crontab("root", NULL, SYSCRONTAB, &new_db, old_db);
#endif
if (!(dir = opendir(SYS_CROND_DIR))) {
log_it("CRON", pid, "OPENDIR FAILED", SYS_CROND_DIR, errno);
}
else {
while (NULL != (dp = readdir(dir))) {
char tabname[NAME_MAX + 1];
if (not_a_crontab(dp))
continue;
if (!glue_strings(tabname, sizeof tabname, SYS_CROND_DIR,
dp->d_name, '/'))
continue;
process_crontab("root", NULL, tabname, &new_db, old_db);
}
closedir(dir);
}
if (!(dir = opendir(SPOOL_DIR))) {
log_it("CRON", pid, "OPENDIR FAILED", SPOOL_DIR, errno);
}
else {
while (NULL != (dp = readdir(dir))) {
char fname[NAME_MAX + 1], tabname[NAME_MAX + 1];
if (not_a_crontab(dp))
continue;
strncpy(fname, dp->d_name, NAME_MAX + 1);
if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR,
dp->d_name, '/'))
continue;
process_crontab(fname, fname, tabname, &new_db, old_db);
}
closedir(dir);
}
/* if we don't do this, then when our children eventually call
* getpwnam() in do_command.c's child_process to verify MAILTO=,
* they will screw us up (and v-v).
*/
endpwent();
}
else {
/* just return as no db reload is needed */
return;
}
overwrite_database(old_db, &new_db);
Debug(DLOAD, ("check_inotify_database is done\n"));
}
#endif
static void overwrite_database(cron_db * old_db, cron_db * new_db) {
user *u, *nu;
/* whatever's left in the old database is now junk.
*/
Debug(DLOAD, ("unlinking old database:\n"));
for (u = old_db->head; u != NULL; u = nu) {
Debug(DLOAD, ("\t%s\n", u->name));
nu = u->next;
unlink_user(old_db, u);
free_user(u);
}
/* overwrite the database control block with the new one.
*/
*old_db = *new_db;
}
int load_database(cron_db * old_db) {
struct stat statbuf, syscron_stat, crond_stat;
cron_db new_db;
DIR_T *dp;
DIR *dir;
pid_t pid = getpid();
int is_local = 0;
time_t now;
Debug(DLOAD, ("[%ld] load_database()\n", (long) pid));
now = time(NULL);
/* before we start loading any data, do a stat on SPOOL_DIR
* so that if anything changes as of this moment (i.e., before we've
* cached any of the database), we'll see the changes next time.
*/
if (stat(SPOOL_DIR, &statbuf) < OK) {
log_it("CRON", pid, "STAT FAILED", SPOOL_DIR, errno);
statbuf.st_mtime = 0;
}
else {
max_mtime(SPOOL_DIR, &statbuf);
}
if (stat(SYS_CROND_DIR, &crond_stat) < OK) {
log_it("CRON", pid, "STAT FAILED", SYS_CROND_DIR, errno);
crond_stat.st_mtime = 0;
}
else {
max_mtime(SYS_CROND_DIR, &crond_stat);
}
#if defined ENABLE_SYSCRONTAB
/* track system crontab file
*/
if (stat(SYSCRONTAB, &syscron_stat) < OK)
syscron_stat.st_mtime = 0;
#endif
/* if spooldir's mtime has not changed, we don't need to fiddle with
* the database.
*
* Note that old_db->mtime is initialized to 0 in main().
*
* We also use now - 1 as the upper bound of timestamp to avoid race,
* when a crontab is updated twice in a single second when we are
* just reading it.
*/
if (old_db->mtime != 0
&& old_db->mtime == TMIN(now - 1,
TMAX(crond_stat.st_mtime,
TMAX(statbuf.st_mtime, syscron_stat.st_mtime)))
) {
Debug(DLOAD, ("[%ld] spool dir mtime unch, no load needed.\n",
(long) pid));
return 0;
}
/* something's different. make a new database, moving unchanged
* elements from the old database, reloading elements that have
* actually changed. Whatever is left in the old database when
* we're done is chaff -- crontabs that disappeared.
*/
new_db.mtime = now - 1;
new_db.head = new_db.tail = NULL;
#if defined WITH_INOTIFY
new_db.ifd = old_db->ifd;
#endif
#if defined ENABLE_SYSCRONTAB
if (syscron_stat.st_mtime)
process_crontab("root", NULL, SYSCRONTAB, &new_db, old_db);
#endif
if (!(dir = opendir(SYS_CROND_DIR))) {
log_it("CRON", pid, "OPENDIR FAILED", SYS_CROND_DIR, errno);
}
else {
while (NULL != (dp = readdir(dir))) {
char tabname[NAME_MAX + 1];
if (not_a_crontab(dp))
continue;
if (!glue_strings(tabname, sizeof tabname, SYS_CROND_DIR,
dp->d_name, '/'))
continue; /* XXX log? */
process_crontab("root", NULL, tabname, &new_db, old_db);
}
closedir(dir);
}
/* we used to keep this dir open all the time, for the sake of
* efficiency. however, we need to close it in every fork, and
* we fork a lot more often than the mtime of the dir changes.
*/
if (!(dir = opendir(SPOOL_DIR))) {
log_it("CRON", pid, "OPENDIR FAILED", SPOOL_DIR, errno);
}
else {
is_local = cluster_host_is_local();
while (is_local && NULL != (dp = readdir(dir))) {
char fname[NAME_MAX + 1], tabname[NAME_MAX + 1];
if (not_a_crontab(dp))
continue;
strncpy(fname, dp->d_name, NAME_MAX);
fname[NAME_MAX] = '\0';
if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, fname, '/'))
continue; /* XXX log? */
process_crontab(fname, fname, tabname, &new_db, old_db);
}
closedir(dir);
}
/* if we don't do this, then when our children eventually call
* getpwnam() in do_command.c's child_process to verify MAILTO=,
* they will screw us up (and v-v).
*/
endpwent();
overwrite_database(old_db, &new_db);
Debug(DLOAD, ("load_database is done\n"));
return 1;
}
void link_user(cron_db * db, user * u) {
if (db->head == NULL)
db->head = u;
if (db->tail)
db->tail->next = u;
u->prev = db->tail;
u->next = NULL;
db->tail = u;
}
void unlink_user(cron_db * db, user * u) {
if (u->prev == NULL)
db->head = u->next;
else
u->prev->next = u->next;
if (u->next == NULL)
db->tail = u->prev;
else
u->next->prev = u->prev;
}
user *find_user(cron_db * db, const char *name, const char *tabname) {
user *u;
for (u = db->head; u != NULL; u = u->next)
if ((strcmp(u->name, name) == 0)
&& ((tabname == NULL)
|| (strcmp(tabname, u->tabname) == 0)
)
)
break;
return (u);
}
static int not_a_crontab(DIR_T * dp) {
size_t len;
/* avoid file names beginning with ".". this is good
* because we would otherwise waste two guaranteed calls
* to getpwnam() for . and .., and there shouldn't be
* hidden files in here anyway
*/
if (dp->d_name[0] == '.')
return (1);
/* ignore files starting with # and ending with ~ */
if (dp->d_name[0] == '#')
return (1);
/* ignore CRON_HOSTNAME file (in case doesn't start with ".") */
if (0 == strcmp(dp->d_name, CRON_HOSTNAME))
return(1);
len = strlen(dp->d_name);
if (len >= NAME_MAX || len == 0)
return (1);
if (dp->d_name[len - 1] == '~')
return (1);
if ((len > 8) && (strncmp(dp->d_name + len - 8, ".rpmsave", 8) == 0))
return (1);
if ((len > 8) && (strncmp(dp->d_name + len - 8, ".rpmorig", 8) == 0))
return (1);
if ((len > 7) && (strncmp(dp->d_name + len - 7, ".rpmnew", 7) == 0))
return (1);
return (0);
}
static void max_mtime(const char *dir_name, struct stat *max_st) {
DIR *dir;
DIR_T *dp;
struct stat st;
if (!(dir = opendir(dir_name))) {
max_st->st_mtime = 0;
return;
}
while (NULL != (dp = readdir(dir))) {
char tabname[NAME_MAX + 1];
if ( not_a_crontab ( dp ) && strcmp(dp->d_name, CRON_HOSTNAME) != 0)
continue;
if (!glue_strings(tabname, sizeof tabname, dir_name, dp->d_name, '/'))
continue; /* XXX log? */
if (stat(tabname, &st) < OK)
continue; /* XXX log? */
if (st.st_mtime > max_st->st_mtime)
max_st->st_mtime = st.st_mtime;
}
closedir(dir);
}

641
src/do_command.c Normal file
View File

@ -0,0 +1,641 @@
/* Copyright 1988,1990,1993,1994 by Paul Vixie
* All rights reserved
*/
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
*
* 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.
*/
#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#include "externs.h"
#include "funcs.h"
#include "globals.h"
#include "structs.h"
#include "cronie_common.h"
#ifndef isascii
# define isascii(c) ((unsigned)(c)<=0177)
#endif
static int child_process(entry *, char **);
static int safe_p(const char *, const char *);
void do_command(entry * e, user * u) {
pid_t pid = getpid();
int ev;
char **jobenv = NULL;
Debug(DPROC, ("[%ld] do_command(%s, (%s,%ld,%ld))\n",
(long) pid, e->cmd, u->name,
(long) e->pwd->pw_uid, (long) e->pwd->pw_gid));
/* fork to become asynchronous -- parent process is done immediately,
* and continues to run the normal cron code, which means return to
* tick(). the child and grandchild don't leave this function, alive.
*
* vfork() is unsuitable, since we have much to do, and the parent
* needs to be able to run off and fork other processes.
*/
switch (fork()) {
case -1:
log_it("CRON", pid, "CAN'T FORK", "do_command", errno);
break;
case 0:
/* child process */
acquire_daemonlock(1);
/* Set up the Red Hat security context for both mail/minder and job processes:
*/
if (cron_set_job_security_context(e, u, &jobenv) != 0) {
_exit(ERROR_EXIT);
}
ev = child_process(e, jobenv);
#ifdef WITH_PAM
cron_close_pam();
#endif
env_free(jobenv);
Debug(DPROC, ("[%ld] child process done, exiting\n", (long) getpid()));
_exit(ev);
break;
default:
/* parent process */
break;
}
Debug(DPROC, ("[%ld] main process returning to work\n", (long) pid));
}
static int child_process(entry * e, char **jobenv) {
int stdin_pipe[2], stdout_pipe[2];
char *input_data, *usernm, *mailto, *mailfrom;
char mailto_expanded[MAX_EMAILSTR];
char mailfrom_expanded[MAX_EMAILSTR];
int children = 0;
pid_t pid = getpid();
struct sigaction sa;
/* Ignore SIGPIPE as we will be writing to pipes and do not want to terminate
prematurely */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
/* our parent is watching for our death by catching SIGCHLD. we
* do not care to watch for our children's deaths this way -- we
* use wait() explicitly. so we have to reset the signal (which
* was inherited from the parent).
*/
sa.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &sa, NULL);
Debug(DPROC, ("[%ld] child_process('%s')\n", (long) getpid(), e->cmd));
#ifdef CAPITALIZE_FOR_PS
/* mark ourselves as different to PS command watchers by upshifting
* our program name. This has no effect on some kernels.
*/
/*local */ {
char *pch;
for (pch = ProgramName; *pch; pch++)
*pch = (char)MkUpper(*pch);
}
#endif /* CAPITALIZE_FOR_PS */
/* discover some useful and important environment settings
*/
usernm = e->pwd->pw_name;
mailto = env_get("MAILTO", jobenv);
mailfrom = env_get("MAILFROM", e->envp);
if (mailto != NULL) {
if (expand_envvar(mailto, mailto_expanded, sizeof(mailto_expanded))) {
mailto = mailto_expanded;
}
else {
log_it("CRON", pid, "WARNING", "The environment variable 'MAILTO' could not be expanded. The non-expanded value will be used." , 0);
}
}
if (mailfrom != NULL) {
if (expand_envvar(mailfrom, mailfrom_expanded, sizeof(mailfrom_expanded))) {
mailfrom = mailfrom_expanded;
}
else {
log_it("CRON", pid, "WARNING", "The environment variable 'MAILFROM' could not be expanded. The non-expanded value will be used." , 0);
}
}
/* create some pipes to talk to our future child
*/
if (pipe(stdin_pipe) == -1) { /* child's stdin */
log_it("CRON", pid, "PIPE() FAILED", "stdin_pipe", errno);
return ERROR_EXIT;
}
if (pipe(stdout_pipe) == -1) { /* child's stdout */
log_it("CRON", pid, "PIPE() FAILED", "stdout_pipe", errno);
return ERROR_EXIT;
}
/* since we are a forked process, we can diddle the command string
* we were passed -- nobody else is going to use it again, right?
*
* if a % is present in the command, previous characters are the
* command, and subsequent characters are the additional input to
* the command. An escaped % will have the escape character stripped
* from it. Subsequent %'s will be transformed into newlines,
* but that happens later.
*/
/*local */ {
int escaped = FALSE;
int ch;
char *p;
for (input_data = p = e->cmd;
(ch = *input_data) != '\0'; input_data++, p++) {
if (p != input_data)
*p = (char)ch;
if (escaped) {
if (ch == '%')
*--p = (char)ch;
escaped = FALSE;
continue;
}
if (ch == '\\') {
escaped = TRUE;
continue;
}
if (ch == '%') {
*input_data++ = '\0';
break;
}
}
*p = '\0';
}
/* fork again, this time so we can exec the user's command.
*/
switch (fork()) {
case -1:
log_it("CRON", pid, "CAN'T FORK", "child_process", errno);
return ERROR_EXIT;
/*NOTREACHED*/
case 0:
Debug(DPROC, ("[%ld] grandchild process fork()'ed\n", (long) getpid()));
/* write a log message. we've waited this long to do it
* because it was not until now that we knew the PID that
* the actual user command shell was going to get and the
* PID is part of the log message.
*/
if ((e->flags & DONT_LOG) == 0) {
char *x = mkprints((u_char *) e->cmd, strlen(e->cmd));
if (x == NULL) /* out of memory, better exit */
_exit(ERROR_EXIT);
log_it(usernm, getpid(), "CMD", x, 0);
free(x);
}
if (cron_change_user_permanently(e->pwd, env_get("HOME", jobenv)) < 0)
_exit(ERROR_EXIT);
/* get new pgrp, void tty, etc.
*/
(void) setsid();
/* reset the SIGPIPE back to default so the child will terminate
* if it tries to write to a closed pipe
*/
sa.sa_handler = SIG_DFL;
sigaction(SIGPIPE, &sa, NULL);
/* close the pipe ends that we won't use. this doesn't affect
* the parent, who has to read and write them; it keeps the
* kernel from recording us as a potential client TWICE --
* which would keep it from sending SIGPIPE in otherwise
* appropriate circumstances.
*/
close(stdin_pipe[WRITE_PIPE]);
close(stdout_pipe[READ_PIPE]);
/* grandchild process. make std{in,out} be the ends of
* pipes opened by our daddy; make stderr go to stdout.
*/
if (stdin_pipe[READ_PIPE] != STDIN) {
dup2(stdin_pipe[READ_PIPE], STDIN);
close(stdin_pipe[READ_PIPE]);
}
if (stdout_pipe[WRITE_PIPE] != STDOUT) {
dup2(stdout_pipe[WRITE_PIPE], STDOUT);
close(stdout_pipe[WRITE_PIPE]);
}
dup2(STDOUT, STDERR);
/*
* Exec the command.
*/
{
char *shell = env_get("SHELL", jobenv);
int fd, fdmax = TMIN(getdtablesize(), MAX_CLOSE_FD);
/* close all unwanted open file descriptors */
for(fd = STDERR + 1; fd < fdmax; fd++) {
close(fd);
}
#if DEBUGGING
if (DebugFlags & DTEST) {
fprintf(stderr, "debug DTEST is on, not exec'ing command.\n");
fprintf(stderr, "\tcmd='%s' shell='%s'\n", e->cmd, shell);
_exit(OK_EXIT);
}
#endif /*DEBUGGING*/
execle(shell, shell, "-c", e->cmd, (char *) 0, jobenv);
fprintf(stderr, "execl: couldn't exec `%s'\n", shell);
perror("execl");
_exit(ERROR_EXIT);
}
break;
default:
cron_restore_default_security_context();
/* parent process */
break;
}
children++;
/* middle process, child of original cron, parent of process running
* the user's command.
*/
Debug(DPROC, ("[%ld] child continues, closing pipes\n", (long) getpid()));
/* close the ends of the pipe that will only be referenced in the
* grandchild process...
*/
close(stdin_pipe[READ_PIPE]);
close(stdout_pipe[WRITE_PIPE]);
/*
* write, to the pipe connected to child's stdin, any input specified
* after a % in the crontab entry. while we copy, convert any
* additional %'s to newlines. when done, if some characters were
* written and the last one wasn't a newline, write a newline.
*
* Note that if the input data won't fit into one pipe buffer (2K
* or 4K on most BSD systems), and the child doesn't read its stdin,
* we would block here. thus we must fork again.
*/
if (*input_data && fork() == 0) {
FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
int need_newline = FALSE;
int escaped = FALSE;
int ch;
Debug(DPROC, ("[%ld] child2 sending data to grandchild\n",
(long) getpid()));
/* reset the SIGPIPE back to default so the child will terminate
* if it tries to write to a closed pipe
*/
sa.sa_handler = SIG_DFL;
sigaction(SIGPIPE, &sa, NULL);
/* close the pipe we don't use, since we inherited it and
* are part of its reference count now.
*/
close(stdout_pipe[READ_PIPE]);
if (cron_change_user_permanently(e->pwd, env_get("HOME", jobenv)) < 0)
_exit(ERROR_EXIT);
/* translation:
* \% -> %
* % -> \n
* \x -> \x for all x != %
*/
while ((ch = *input_data++) != '\0') {
if (escaped) {
if (ch != '%')
putc('\\', out);
}
else {
if (ch == '%')
ch = '\n';
}
if (!(escaped = (ch == '\\'))) {
putc(ch, out);
need_newline = (ch != '\n');
}
}
if (escaped)
putc('\\', out);
if (need_newline)
putc('\n', out);
/* close the pipe, causing an EOF condition. fclose causes
* stdin_pipe[WRITE_PIPE] to be closed, too.
*/
fclose(out);
Debug(DPROC, ("[%ld] child2 done sending to grandchild\n",
(long) getpid()));
_exit(0);
}
/* close the pipe to the grandkiddie's stdin, since its wicked uncle
* ernie back there has it open and will close it when he's done.
*/
close(stdin_pipe[WRITE_PIPE]);
children++;
/*
* read output from the grandchild. it's stderr has been redirected to
* it's stdout, which has been redirected to our pipe. if there is any
* output, we'll be mailing it to the user whose crontab this is...
* when the grandchild exits, we'll get EOF.
*/
Debug(DPROC, ("[%ld] child reading output from grandchild\n",
(long) getpid()));
/*local */ {
FILE *in = fdopen(stdout_pipe[READ_PIPE], "r");
int ch = getc(in);
if (ch != EOF) {
FILE *mail = NULL;
int bytes = 1;
int status = 0;
#if defined(SYSLOG)
char logbuf[1024];
int bufidx = 0;
if (SyslogOutput) {
if (ch != '\n')
logbuf[bufidx++] = (char)ch;
}
#endif
Debug(DPROC | DEXT,
("[%ld] got data (%x:%c) from grandchild\n",
(long) getpid(), ch, ch));
/* get name of recipient. this is MAILTO if set to a
* valid local username; USER otherwise.
*/
if (mailto) {
/* MAILTO was present in the environment
*/
if (!*mailto) {
/* ... but it's empty. set to NULL
*/
mailto = NULL;
}
}
else {
/* MAILTO not present, set to USER.
*/
mailto = usernm;
}
/* get sender address. this is MAILFROM if set (and safe),
* the user account name otherwise.
*/
if (!mailfrom || !*mailfrom || !safe_p(usernm, mailfrom)) {
mailfrom = e->pwd->pw_name;
}
/* if we are supposed to be mailing, MAILTO will
* be non-NULL. only in this case should we set
* up the mail command and subjects and stuff...
*/
/* Also skip it if MailCmd is set to "off" */
if (mailto && safe_p(usernm, mailto)
&& strncmp(MailCmd,"off",3) && !SyslogOutput) {
char **env;
char mailcmd[MAX_COMMAND+1]; /* +1 for terminator */
char hostname[MAXHOSTNAMELEN];
char *content_type = env_get("CONTENT_TYPE", jobenv),
*content_transfer_encoding =
env_get("CONTENT_TRANSFER_ENCODING", jobenv);
gethostname(hostname, MAXHOSTNAMELEN);
if (MailCmd[0] == '\0') {
int len;
len = snprintf(mailcmd, sizeof mailcmd, MAILFMT, MAILARG, mailfrom);
if (len < 0) {
fprintf(stderr, "mailcmd snprintf failed\n");
(void) _exit(ERROR_EXIT);
}
if (sizeof mailcmd <= (size_t) len) {
fprintf(stderr, "mailcmd too long\n");
(void) _exit(ERROR_EXIT);
}
}
else {
strncpy(mailcmd, MailCmd, MAX_COMMAND+1);
}
if (!(mail = cron_popen(mailcmd, "w", e->pwd, jobenv))) {
perror(mailcmd);
(void) _exit(ERROR_EXIT);
}
fprintf(mail, "From: \"(Cron Daemon)\" <%s>\n", mailfrom);
fprintf(mail, "To: %s\n", mailto);
fprintf(mail, "Subject: Cron <%s@%s> %s\n",
usernm, first_word(hostname, "."), e->cmd);
#ifdef MAIL_DATE
fprintf(mail, "Date: %s\n", arpadate(&StartTime));
#endif /*MAIL_DATE */
fprintf(mail, "MIME-Version: 1.0\n");
if (content_type == NULL) {
fprintf(mail, "Content-Type: text/plain; charset=%s\n",
cron_default_mail_charset);
}
else { /* user specified Content-Type header.
* disallow new-lines for security reasons
* (else users could specify arbitrary mail headers!)
*/
char *nl = content_type;
size_t ctlen = strlen(content_type);
while ((*nl != '\0')
&& ((nl = strchr(nl, '\n')) != NULL)
&& (nl < (content_type + ctlen))
)
*nl = ' ';
fprintf(mail, "Content-Type: %s\n", content_type);
}
if (content_transfer_encoding == NULL) {
fprintf(mail, "Content-Transfer-Encoding: 8bit\n");
}
else {
char *nl = content_transfer_encoding;
size_t ctlen = strlen(content_transfer_encoding);
while ((*nl != '\0')
&& ((nl = strchr(nl, '\n')) != NULL)
&& (nl < (content_transfer_encoding + ctlen))
)
*nl = ' ';
fprintf(mail, "Content-Transfer-Encoding: %s\n",
content_transfer_encoding);
}
/* The Auto-Submitted header is
* defined (and suggested by) RFC3834.
*/
fprintf(mail, "Auto-Submitted: auto-generated\n");
fprintf(mail, "Precedence: bulk\n");
for (env = jobenv; *env; env++)
fprintf(mail, "X-Cron-Env: <%s>\n", *env);
fprintf(mail, "\n");
/* this was the first char from the pipe
*/
putc(ch, mail);
}
/* we have to read the input pipe no matter whether
* we mail or not, but obviously we only write to
* mail pipe if we ARE mailing.
*/
while (EOF != (ch = getc(in))) {
if (ch == '\r')
continue;
bytes++;
if (mail)
putc(ch, mail);
#if defined(SYSLOG)
if (SyslogOutput) {
logbuf[bufidx++] = (char)ch;
if ((ch == '\n') || (bufidx == sizeof(logbuf)-1)) {
if (ch == '\n')
logbuf[bufidx-1] = '\0';
else
logbuf[bufidx] = '\0';
log_it(usernm, getpid(), "CMDOUT", logbuf, 0);
bufidx = 0;
}
}
#endif
}
/* only close pipe if we opened it -- i.e., we're
* mailing...
*/
if (mail) {
Debug(DPROC, ("[%ld] closing pipe to mail\n", (long) getpid()));
/* Note: the pclose will probably see
* the termination of the grandchild
* in addition to the mail process, since
* it (the grandchild) is likely to exit
* after closing its stdout.
*/
status = cron_pclose(mail);
}
#if defined(SYSLOG)
if (SyslogOutput) {
if (bufidx) {
logbuf[bufidx] = '\0';
log_it(usernm, getpid(), "CMDOUT", logbuf, 0);
}
}
#endif
/* if there was output and we could not mail it,
* log the facts so the poor user can figure out
* what's going on.
*/
if (mail && status && !SyslogOutput) {
char buf[MAX_TEMPSTR];
sprintf(buf,
"mailed %d byte%s of output but got status 0x%04x\n",
bytes, (bytes == 1) ? "" : "s", status);
log_it(usernm, getpid(), "MAIL", buf, 0);
}
} /*if data from grandchild */
Debug(DPROC, ("[%ld] got EOF from grandchild\n", (long) getpid()));
fclose(in); /* also closes stdout_pipe[READ_PIPE] */
}
/* wait for children to die.
*/
for (; children > 0; children--) {
WAIT_T waiter;
PID_T child;
Debug(DPROC, ("[%ld] waiting for grandchild #%d to finish\n",
(long) getpid(), children));
while ((child = wait(&waiter)) < OK && errno == EINTR) ;
if (child < OK) {
Debug(DPROC,
("[%ld] no more grandchildren--mail written?\n",
(long) getpid()));
break;
}
Debug(DPROC, ("[%ld] grandchild #%ld finished, status=%04x",
(long) getpid(), (long) child, WEXITSTATUS(waiter)));
if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
Debug(DPROC, (", dumped core"));
Debug(DPROC, ("\n"));
}
if ((e->flags & DONT_LOG) == 0) {
char *x = mkprints((u_char *) e->cmd, strlen(e->cmd));
log_it(usernm, getpid(), "CMDEND", x ? x : "**Unknown command**" , 0);
free(x);
}
return OK_EXIT;
}
static int safe_p(const char *usernm, const char *s) {
static const char safe_delim[] = "@!:%-.,_+"; /* conservative! */
const char *t;
int ch, first;
for (t = s, first = 1; (ch = *t++) != '\0'; first = 0) {
if (isascii(ch) && isprint(ch) &&
(isalnum(ch) || (!first && strchr(safe_delim, ch))))
continue;
log_it(usernm, getpid(), "UNSAFE", s, 0);
return (FALSE);
}
return (TRUE);
}

711
src/entry.c Normal file
View File

@ -0,0 +1,711 @@
/*
* Copyright 1988,1990,1993,1994 by Paul Vixie
* All rights reserved
*/
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
*
* 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.
*/
/* vix 26jan87 [RCS'd; rest of log is in RCS file]
* vix 01jan87 [added line-level error recovery]
* vix 31dec86 [added /step to the from-to range, per bob@acornrc]
* vix 30dec86 [written]
*/
#include "config.h"
#include <ctype.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "bitstring.h"
#include "funcs.h"
#include "globals.h"
#include "macros.h"
#include "pathnames.h"
typedef enum ecode {
e_none, e_minute, e_hour, e_dom, e_month, e_dow,
e_cmd, e_timespec, e_username, e_option, e_memory
} ecode_e;
static const char *ecodes[] = {
"no error",
"bad minute",
"bad hour",
"bad day-of-month",
"bad month",
"bad day-of-week",
"bad command",
"bad time specifier",
"bad username",
"bad option",
"out of memory"
};
typedef enum {
R_START,
R_AST,
R_STEP,
R_TERMS,
R_NUM1,
R_RANGE,
R_RANGE_NUM2,
R_RANDOM,
R_RANDOM_NUM2,
R_FINISH,
} range_state_t;
static int get_list(bitstr_t *, int, int, const char *[], int, FILE *),
get_range(bitstr_t *, int, int, const char *[], FILE *),
get_number(int *, int, const char *[], FILE *),
set_element(bitstr_t *, int, int, int);
void free_entry(entry * e) {
free(e->cmd);
free(e->pwd);
env_free(e->envp);
free(e);
}
/* return NULL if eof or syntax error occurs;
* otherwise return a pointer to a new entry.
*/
entry *load_entry(FILE * file, void (*error_func) (), struct passwd *pw,
char **envp) {
/* this function reads one crontab entry -- the next -- from a file.
* it skips any leading blank lines, ignores comments, and returns
* NULL if for any reason the entry can't be read and parsed.
*
* the entry is also parsed here.
*
* syntax:
* user crontab:
* minutes hours doms months dows cmd\n
* system crontab (/etc/crontab):
* minutes hours doms months dows USERNAME cmd\n
*/
ecode_e ecode = e_none;
entry *e = NULL;
int ch;
char cmd[MAX_COMMAND];
char envstr[MAX_ENVSTR];
char **tenvp;
char *p;
struct passwd temppw;
int i;
Debug(DPARS, ("load_entry()...about to eat comments\n"));
ch = get_char(file);
if (ch == EOF)
return (NULL);
/* ch is now the first useful character of a useful line.
* it may be an @special or it may be the first character
* of a list of minutes.
*/
e = (entry *) calloc(sizeof (entry), sizeof (char));
if (e == NULL) {
ecode = e_memory;
goto eof;
}
/* check for '-' as a first character, this option will disable
* writing a syslog message about command getting executed
*/
if (ch == '-') {
/* if we are editing system crontab or user uid is 0 (root)
* we are allowed to disable logging
*/
if (pw == NULL || pw->pw_uid == 0)
e->flags |= DONT_LOG;
else {
log_it("CRON", getpid(), "ERROR", "Only privileged user can disable logging", 0);
ecode = e_option;
goto eof;
}
ch = get_char(file);
if (ch == EOF) {
free(e);
return NULL;
}
}
if (ch == '@') {
/* all of these should be flagged and load-limited; i.e.,
* instead of @hourly meaning "0 * * * *" it should mean
* "close to the front of every hour but not 'til the
* system load is low". Problems are: how do you know
* what "low" means? (save me from /etc/cron.conf!) and:
* how to guarantee low variance (how low is low?), which
* means how to we run roughly every hour -- seems like
* we need to keep a history or let the first hour set
* the schedule, which means we aren't load-limited
* anymore. too much for my overloaded brain. (vix, jan90)
* HINT
*/
ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
if (!strcmp("reboot", cmd)) {
e->flags |= WHEN_REBOOT;
}
else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)) {
bit_set(e->minute, 0);
bit_set(e->hour, 0);
bit_set(e->dom, 0);
bit_set(e->month, 0);
bit_nset(e->dow, 0, LAST_DOW - FIRST_DOW);
e->flags |= DOW_STAR;
}
else if (!strcmp("monthly", cmd)) {
bit_set(e->minute, 0);
bit_set(e->hour, 0);
bit_set(e->dom, 0);
bit_nset(e->month, 0, LAST_MONTH - FIRST_MONTH);
bit_nset(e->dow, 0, LAST_DOW - FIRST_DOW);
e->flags |= DOW_STAR;
}
else if (!strcmp("weekly", cmd)) {
bit_set(e->minute, 0);
bit_set(e->hour, 0);
bit_nset(e->dom, 0, LAST_DOM - FIRST_DOM);
bit_nset(e->month, 0, LAST_MONTH - FIRST_MONTH);
bit_set(e->dow, 0);
e->flags |= DOM_STAR;
}
else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
bit_set(e->minute, 0);
bit_set(e->hour, 0);
bit_nset(e->dom, 0, LAST_DOM - FIRST_DOM);
bit_nset(e->month, 0, LAST_MONTH - FIRST_MONTH);
bit_nset(e->dow, 0, LAST_DOW - FIRST_DOW);
}
else if (!strcmp("hourly", cmd)) {
bit_set(e->minute, 0);
bit_nset(e->hour, 0, LAST_HOUR - FIRST_HOUR);
bit_nset(e->dom, 0, LAST_DOM - FIRST_DOM);
bit_nset(e->month, 0, LAST_MONTH - FIRST_MONTH);
bit_nset(e->dow, 0, LAST_DOW - FIRST_DOW);
e->flags |= HR_STAR;
}
else {
ecode = e_timespec;
goto eof;
}
/* Advance past whitespace between shortcut and
* username/command.
*/
Skip_Blanks(ch, file);
if (ch == EOF || ch == '\n') {
ecode = e_cmd;
goto eof;
}
}
else {
Debug(DPARS, ("load_entry()...about to parse numerics\n"));
if (ch == '*')
e->flags |= MIN_STAR;
ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, PPC_NULL, ch, file);
if (ch == EOF) {
ecode = e_minute;
goto eof;
}
/* hours
*/
if (ch == '*')
e->flags |= HR_STAR;
ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR, PPC_NULL, ch, file);
if (ch == EOF) {
ecode = e_hour;
goto eof;
}
/* DOM (days of month)
*/
if (ch == '*')
e->flags |= DOM_STAR;
ch = get_list(e->dom, FIRST_DOM, LAST_DOM, PPC_NULL, ch, file);
if (ch == EOF) {
ecode = e_dom;
goto eof;
}
/* month
*/
ch = get_list(e->month, FIRST_MONTH, LAST_MONTH, MonthNames, ch, file);
if (ch == EOF) {
ecode = e_month;
goto eof;
}
/* DOW (days of week)
*/
if (ch == '*')
e->flags |= DOW_STAR;
ch = get_list(e->dow, FIRST_DOW, LAST_DOW, DowNames, ch, file);
if (ch == EOF) {
ecode = e_dow;
goto eof;
}
}
/* make sundays equivalent */
if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) {
bit_set(e->dow, 0);
bit_set(e->dow, 7);
}
/* check for permature EOL and catch a common typo */
if (ch == '\n' || ch == '*') {
ecode = e_cmd;
goto eof;
}
/* ch is the first character of a command, or a username */
unget_char(ch, file);
if (!pw) {
char *username = cmd; /* temp buffer */
Debug(DPARS, ("load_entry()...about to parse username\n"));
ch = get_string(username, MAX_COMMAND, file, " \t\n");
Debug(DPARS, ("load_entry()...got %s\n", username));
if (ch == EOF || ch == '\n' || ch == '*') {
ecode = e_cmd;
goto eof;
}
pw = getpwnam(username);
if (pw == NULL) {
Debug(DPARS, ("load_entry()...unknown user entry\n"));
memset(&temppw, 0, sizeof (temppw));
temppw.pw_name = username;
temppw.pw_passwd = "";
pw = &temppw;
} else {
Debug(DPARS, ("load_entry()...uid %ld, gid %ld\n",
(long) pw->pw_uid, (long) pw->pw_gid));
}
/* Advance past whitespace before command. */
Skip_Blanks(ch, file);
/* check for permature EOL or EOF */
if (ch == EOF || ch == '\n') {
ecode = e_cmd;
goto eof;
}
/* ch is the first character of a command */
unget_char(ch, file);
}
if ((e->pwd = pw_dup(pw)) == NULL) {
ecode = e_memory;
goto eof;
}
memset(e->pwd->pw_passwd, 0, strlen(e->pwd->pw_passwd));
p = env_get("RANDOM_DELAY", envp);
if (p) {
char *endptr;
long val;
errno = 0; /* To distinguish success/failure after call */
val = strtol(p, &endptr, 10);
if (errno != 0 || val < 0 || val > 24*60) {
log_it("CRON", getpid(), "ERROR", "bad value of RANDOM_DELAY", 0);
} else {
e->delay = (int)((double)val * RandomScale);
}
}
/* copy and fix up environment. some variables are just defaults and
* others are overrides.
*/
if ((e->envp = env_copy(envp)) == NULL) {
ecode = e_memory;
goto eof;
}
if (!env_get("SHELL", e->envp)) {
if (glue_strings(envstr, sizeof envstr, "SHELL", _PATH_BSHELL, '=')) {
if ((tenvp = env_set(e->envp, envstr)) == NULL) {
ecode = e_memory;
goto eof;
}
e->envp = tenvp;
}
else
log_it("CRON", getpid(), "ERROR", "can't set SHELL", 0);
}
if ((tenvp = env_update_home(e->envp, pw->pw_dir)) == NULL) {
ecode = e_memory;
goto eof;
}
e->envp = tenvp;
#ifndef LOGIN_CAP
/* If login.conf is in used we will get the default PATH later. */
if (!env_get("PATH", e->envp)) {
char *defpath;
if (ChangePath)
defpath = _PATH_DEFPATH;
else {
defpath = getenv("PATH");
if (defpath == NULL)
defpath = _PATH_DEFPATH;
}
if (glue_strings(envstr, sizeof envstr, "PATH", defpath, '=')) {
if ((tenvp = env_set(e->envp, envstr)) == NULL) {
ecode = e_memory;
goto eof;
}
e->envp = tenvp;
}
else
log_it("CRON", getpid(), "ERROR", "can't set PATH", 0);
}
#endif /* LOGIN_CAP */
if (glue_strings(envstr, sizeof envstr, "LOGNAME", pw->pw_name, '=')) {
if ((tenvp = env_set(e->envp, envstr)) == NULL) {
ecode = e_memory;
goto eof;
}
e->envp = tenvp;
}
else
log_it("CRON", getpid(), "ERROR", "can't set LOGNAME", 0);
#if defined(BSD) || defined(__linux)
if (glue_strings(envstr, sizeof envstr, "USER", pw->pw_name, '=')) {
if ((tenvp = env_set(e->envp, envstr)) == NULL) {
ecode = e_memory;
goto eof;
}
e->envp = tenvp;
}
else
log_it("CRON", getpid(), "ERROR", "can't set USER", 0);
#endif
Debug(DPARS, ("load_entry()...about to parse command\n"));
/* Everything up to the next \n or EOF is part of the command...
* too bad we don't know in advance how long it will be, since we
* need to malloc a string for it... so, we limit it to MAX_COMMAND.
*/
ch = get_string(cmd, MAX_COMMAND, file, "\n");
/* a file without a \n before the EOF is rude, so we'll complain...
*/
if (ch == EOF) {
ecode = e_cmd;
goto eof;
}
/* got the command in the 'cmd' string; save it in *e.
*/
if ((e->cmd = strdup(cmd)) == NULL) {
ecode = e_memory;
goto eof;
}
Debug(DPARS, ("load_entry()...returning successfully\n"));
/* success, fini, return pointer to the entry we just created...
*/
return (e);
eof:
if (e) {
if (e->envp)
env_free(e->envp);
free(e->pwd);
free(e->cmd);
free(e);
}
for (i = 0; i < MAX_COMMAND && ch != '\n' && !feof(file); i++)
ch = get_char(file);
if (ecode != e_none && error_func)
(*error_func) (ecodes[(int) ecode]);
return (NULL);
}
static int
get_list(bitstr_t * bits, int low, int high, const char *names[],
int ch, FILE * file) {
int done;
/* we know that we point to a non-blank character here;
* must do a Skip_Blanks before we exit, so that the
* next call (or the code that picks up the cmd) can
* assume the same thing.
*/
Debug(DPARS | DEXT, ("get_list()...entered\n"));
/* list = range {"," range}
*/
/* clear the bit string, since the default is 'off'.
*/
bit_nclear(bits, 0, (high - low));
/* process all ranges
*/
done = FALSE;
/* unget ch to allow get_range() to process it properly
*/
unget_char(ch, file);
while (!done) {
if (EOF == (ch = get_range(bits, low, high, names, file)))
return (EOF);
if (ch == ',')
continue;
else
done = TRUE;
}
/* exiting. skip to some blanks, then skip over the blanks.
*/
Skip_Nonblanks(ch, file)
Skip_Blanks(ch, file)
Debug(DPARS | DEXT, ("get_list()...exiting w/ %02x\n", ch));
return (ch);
}
inline static int is_separator(int ch) {
switch (ch) {
case '\t':
case '\n':
case ' ':
case ',':
return 1;
default:
return 0;
}
}
static int
get_range(bitstr_t * bits, int low, int high, const char *names[],
FILE * file) {
/* range = number | number "-" number [ "/" number ]
* | [number] "~" [number]
*/
int ch, i, num1, num2, num3;
/* default value for step
*/
num3 = 1;
range_state_t state = R_START;
while (state != R_FINISH && ((ch = get_char(file)) != EOF)) {
switch (state) {
case R_START:
if (ch == '*') {
num1 = low;
num2 = high;
state = R_AST;
break;
}
if (ch == '~') {
num1 = low;
state = R_RANDOM;
break;
}
unget_char(ch, file);
if (get_number(&num1, low, names, file) != EOF) {
state = R_NUM1;
break;
}
return (EOF);
case R_AST:
if (ch == '/') {
state = R_STEP;
break;
}
if (is_separator(ch)) {
state = R_FINISH;
break;
}
return (EOF);
case R_STEP:
unget_char(ch, file);
if (get_number(&num3, 0, PPC_NULL, file) != EOF
&& num3 != 0) {
state = R_TERMS;
break;
}
return (EOF);
case R_TERMS:
if (is_separator(ch)) {
state = R_FINISH;
break;
}
return (EOF);
case R_NUM1:
if (ch == '-') {
state = R_RANGE;
break;
}
if (ch == '~') {
state = R_RANDOM;
break;
}
if (is_separator(ch)) {
num2 = num1;
state = R_FINISH;
break;
}
return (EOF);
case R_RANGE:
unget_char(ch, file);
if (get_number(&num2, low, names, file) != EOF) {
state = R_RANGE_NUM2;
break;
}
return (EOF);
case R_RANGE_NUM2:
if (ch == '/') {
state = R_STEP;
break;
}
if (is_separator(ch)) {
state = R_FINISH;
break;
}
return (EOF);
case R_RANDOM:
if (is_separator(ch)) {
num2 = high;
state = R_FINISH;
}
else if (unget_char(ch, file),
get_number(&num2, low, names, file) != EOF) {
state = R_TERMS;
}
/* fail if couldn't find match on previous term
*/
else
return (EOF);
/* if invalid random range was selected */
if (num1 > num2)
return (EOF);
/* select random number in range <num1, num2>
*/
num1 = num2 = random() % (num2 - num1 + 1) + num1;
break;
default:
/* We should never get here
*/
return (EOF);
}
}
if (state != R_FINISH || ch == EOF)
return (EOF);
for (i = num1; i <= num2; i += num3)
if (EOF == set_element(bits, low, high, i)) {
unget_char(ch, file);
return (EOF);
}
return ch;
}
static int
get_number(int *numptr, int low, const char *names[], FILE * file) {
char temp[MAX_TEMPSTR], *pc;
int len, i, ch;
char *endptr;
pc = temp;
len = 0;
/* get all alnum characters available */
while (isalnum((ch = get_char(file)))) {
if (++len >= MAX_TEMPSTR)
goto bad;
*pc++ = (char)ch;
}
*pc = '\0';
if (len == 0)
goto bad;
unget_char(ch, file);
/* try to get number */
*numptr = (int) strtol(temp, &endptr, 10);
if (*endptr == '\0' && temp != endptr) {
/* We have a number */
return 0;
}
/* no numbers, look for a string if we have any */
if (names) {
for (i = 0; names[i] != NULL; i++) {
Debug(DPARS | DEXT, ("get_num, compare(%s,%s)\n", names[i], temp));
if (strcasecmp(names[i], temp) == 0) {
*numptr = i + low;
return 0;
}
}
} else {
goto bad;
}
bad:
unget_char(ch, file);
return (EOF);
}
static int set_element(bitstr_t * bits, int low, int high, int number) {
Debug(DPARS | DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number));
if (number < low || number > high)
return (EOF);
bit_set(bits, (number - low));
return (OK);
}

305
src/env.c Normal file
View File

@ -0,0 +1,305 @@
/* Copyright 1988,1990,1993,1994 by Paul Vixie
* All rights reserved
*/
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
*
* 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.
*/
#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "globals.h"
#include "funcs.h"
#if defined(BSD)
extern char **environ;
#endif
char **env_init(void) {
char **p = (char **) malloc(sizeof (char *));
if (p != NULL)
p[0] = NULL;
return (p);
}
void env_free(char **envp) {
char **p;
for (p = envp; *p != NULL; p++)
free(*p);
free(envp);
}
char **env_copy(char **envp) {
int save_errno;
size_t count, i;
char **p;
for (count = 0; envp[count] != NULL; count++) ;
p = (char **) malloc((count + 1) * sizeof (char *)); /* 1 for the NULL */
if (p != NULL) {
for (i = 0; i < count; i++)
if ((p[i] = strdup(envp[i])) == NULL) {
save_errno = errno;
while (i-- > 0)
free(p[i]);
free(p);
errno = save_errno;
return (NULL);
}
p[count] = NULL;
}
return (p);
}
char **env_set(char **envp, const char *envstr) {
size_t count, found;
char **p, *envtmp;
/*
* count the number of elements, including the null pointer;
* also set 'found' to -1 or index of entry if already in here.
*/
found = (size_t)-1;
for (count = 0; envp[count] != NULL; count++) {
if (!strcmp_until(envp[count], envstr, '='))
found = count;
}
count++; /* for the NULL */
if (found != (size_t)-1) {
/*
* it exists already, so just free the existing setting,
* save our new one there, and return the existing array.
*/
if ((envtmp = strdup(envstr)) == NULL)
return (NULL);
free(envp[found]);
envp[found] = envtmp;
return (envp);
}
/*
* it doesn't exist yet, so resize the array, move null pointer over
* one, save our string over the old null pointer, and return resized
* array.
*/
if ((envtmp = strdup(envstr)) == NULL)
return (NULL);
p = (char **) realloc((void *) envp,
(count + 1) * sizeof (char *));
if (p == NULL) {
free(envtmp);
return (NULL);
}
p[count] = p[count - 1];
p[count - 1] = envtmp;
return (p);
}
int env_set_from_environ(char ***envpp) {
static const char *names[] = {
"LANG",
"LC_CTYPE",
"LC_NUMERIC",
"LC_TIME",
"LC_COLLATE",
"LC_MONETARY",
"LC_MESSAGES",
"LC_PAPER",
"LC_NAME",
"LC_ADDRESS",
"LC_TELEPHONE",
"LC_MEASUREMENT",
"LC_IDENTIFICATION",
"LC_ALL",
"LANGUAGE",
"RANDOM_DELAY",
NULL
};
const char **name;
char **procenv;
for (procenv = environ; *procenv != NULL; ++procenv) {
for (name = names; *name != NULL; ++name) {
size_t namelen;
namelen = strlen(*name);
if (strncmp(*name, *procenv, namelen) == 0
&& (*procenv)[namelen] == '=') {
char **tmpenv;
tmpenv = env_set(*envpp, *procenv);
if (tmpenv == NULL)
return FALSE;
*envpp = tmpenv;
}
}
}
return TRUE;
}
/* The following states are used by load_env(), traversed in order: */
enum env_state {
NAMEI, /* First char of NAME, may be quote */
NAME, /* Subsequent chars of NAME */
EQ1, /* After end of name, looking for '=' sign */
EQ2, /* After '=', skipping whitespace */
VALUEI, /* First char of VALUE, may be quote */
VALUE, /* Subsequent chars of VALUE */
FINI, /* All done, skipping trailing whitespace */
ERROR, /* Error */
};
/* return ERR = end of file
* FALSE = not an env setting (file was repositioned)
* TRUE = was an env setting
*/
int load_env(char *envstr, FILE * f) {
long filepos;
int fileline;
enum env_state state;
char quotechar, *c, *str, *val;
filepos = ftell(f);
fileline = LineNumber;
if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n"))
return (ERR);
Debug(DPARS, ("load_env, read <%s>\n", envstr));
val = str = envstr;
state = NAMEI;
quotechar = '\0';
c = envstr;
while (state != ERROR && *c) {
switch (state) {
case NAMEI:
case VALUEI:
if (*c == '\'' || *c == '"')
quotechar = *c++;
state++;
/* FALLTHROUGH */
case NAME:
case VALUE:
if (quotechar) {
if (*c == quotechar) {
state++;
c++;
break;
}
if (state == NAME && *c == '=') {
state = ERROR;
break;
}
}
else {
if (state == NAME) {
if (isspace((unsigned char) *c)) {
c++;
state++;
break;
}
if (*c == '=') {
state++;
break;
}
}
}
*str++ = *c++;
break;
case EQ1:
if (*c == '=') {
state++;
quotechar = '\0';
*str++ = *c;
val = str;
}
else {
if (!isspace((unsigned char) *c))
state = ERROR;
}
c++;
break;
case EQ2:
case FINI:
if (isspace((unsigned char) *c))
c++;
else
state++;
break;
default:
abort();
}
}
if (state != FINI && state != EQ2 && !(state == VALUE && !quotechar)) {
Debug(DPARS, ("load_env, not an env var, state = %d\n", state));
if (fseek(f, filepos, 0)) {
return ERR;
}
Set_LineNum(fileline);
return (FALSE);
}
*str = '\0';
if (state == VALUE) {
/* End of unquoted value: trim trailing whitespace */
while (str > val && isspace((unsigned char)str[-1]))
*(--str) = '\0';
}
return TRUE;
}
char *env_get(const char *name, char **envp) {
size_t len = strlen(name);
char *p, *q;
while ((p = *envp++) != NULL) {
if (!(q = strchr(p, '=')))
continue;
if ((size_t)(q - p) == len && !strncmp(p, name, len))
return (q + 1);
}
return (NULL);
}
char **env_update_home(char **envp, const char *dir) {
char envstr[MAX_ENVSTR];
if (dir == NULL || *dir == '\0' || env_get("HOME", envp)) {
return envp;
}
if (glue_strings(envstr, sizeof envstr, "HOME", dir, '=')) {
envp = env_set(envp, envstr);
}
else
log_it("CRON", getpid(), "ERROR", "can't set HOME", 0);
return envp;
}

91
src/externs.h Normal file
View File

@ -0,0 +1,91 @@
/* Copyright 1993,1994 by Paul Vixie
* All rights reserved
*/
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
*
* 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.
*/
/* reorder these #include's at your peril */
#ifndef CRONIE_EXTERNS_H
#define CRONIE_EXTERNS_H
#if defined(LOGIN_CAP)
# include <login_cap.h>
#endif /*LOGIN_CAP*/
#if defined(BSD_AUTH)
# include <bsd_auth.h>
#endif /*BSD_AUTH*/
#define DIR_T struct dirent
#define WAIT_T int
#define SIG_T sig_t
#define TIME_T time_t
#define PID_T pid_t
#ifndef TZNAME_ALREADY_DEFINED
extern char *tzname[2];
#endif
#define TZONE(tm) tzname[(tm).tm_isdst]
#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(__sun) || defined(_AIX)
# define HAVE_SAVED_UIDS
#endif
#define MY_UID(pw) getuid()
#define MY_GID(pw) getgid()
/* getopt() isn't part of POSIX. some systems define it in <stdlib.h> anyway.
* of those that do, some complain that our definition is different and some
* do not. to add to the misery and confusion, some systems define getopt()
* in ways that we cannot predict or comprehend, yet do not define the adjunct
* external variables needed for the interface.
*/
#if (!defined(BSD) || (BSD < 198911))
int getopt(int, char * const *, const char *);
#endif
#if (!defined(BSD) || (BSD < 199103))
extern char *optarg;
extern int optind, opterr, optopt;
#endif
/* digital unix needs this but does not give us a way to identify it.
*/
extern int flock(int, int);
/* not all systems who provide flock() provide these definitions.
*/
#ifndef LOCK_SH
# define LOCK_SH 1
#endif
#ifndef LOCK_EX
# define LOCK_EX 2
#endif
#ifndef LOCK_NB
# define LOCK_NB 4
#endif
#ifndef LOCK_UN
# define LOCK_UN 8
#endif
#ifndef WCOREDUMP
# define WCOREDUMP(st) (((st) & 0200) != 0)
#endif
#endif /* CRONIE_EXTERNS_H */

131
src/funcs.h Normal file
View File

@ -0,0 +1,131 @@
/*
* $Id: funcs.h,v 1.9 2004/01/23 18:56:42 vixie Exp $
*/
/*
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc.
*
* 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.
*/
/* Notes:
* We should reorg this into sections by module.
*/
#ifndef CRONIE_FUNCS_H
#define CRONIE_FUNCS_H
#include <stdio.h>
#include <sys/types.h>
#ifdef WITH_SELINUX
#include <selinux/selinux.h>
#endif
#include "externs.h"
#include "structs.h"
void set_cron_uid(void),
check_spool_dir(void),
open_logfile(void),
sigpipe_func(void),
job_add(entry *, user *),
do_command(entry *, user *),
link_user(cron_db *, user *),
unlink_user(cron_db *, user *),
free_user(user *),
env_free(char **),
unget_char(int, FILE *),
free_entry(entry *),
acquire_daemonlock(int),
log_it(const char *, PID_T, const char *, const char *, int),
log_close(void),
check_orphans(cron_db *);
#if defined WITH_INOTIFY
void set_cron_watched(int ),
set_cron_unwatched(int ),
check_inotify_database(cron_db *);
#endif
int load_database(cron_db *),
job_runqueue(void),
set_debug_flags(const char *),
get_char(FILE *),
get_string(char *, int, FILE *, const char *),
swap_uids(void),
swap_uids_back(void),
load_env(char *, FILE *),
env_set_from_environ(char ***envpp),
cron_pclose(FILE *),
glue_strings(char *, size_t, const char *, const char *, char),
strcmp_until(const char *, const char *, char),
skip_comments(FILE *),
allowed(const char * ,const char * ,const char *);
size_t strlens(const char *, ...),
strdtb(char *);
char *env_get(const char *, char **),
*arpadate(time_t *),
*mkprints(unsigned char *, size_t),
*first_word(const char *, const char *),
**env_init(void),
**env_copy(char **),
**env_set(char **, const char *),
**env_update_home(char **, const char *);
user *load_user(int, struct passwd *, const char *, const char *, const char *),
*find_user(cron_db *, const char *, const char *);
entry *load_entry(FILE *, void (*)(), struct passwd *, char **);
FILE *cron_popen(char *, const char *, struct passwd *, char **);
struct passwd *pw_dup(const struct passwd *);
#ifndef HAVE_STRUCT_TM_TM_GMTOFF
long get_gmtoff(time_t *, struct tm *);
#endif
/* Red Hat security stuff (security.c):
*/
void cron_restore_default_security_context( void );
int cron_set_job_security_context( entry *e, user *u, char ***jobenvp );
int cron_open_security_session( struct passwd *pw );
void cron_close_security_session( void );
int cron_change_groups( struct passwd *pw );
int cron_change_user_permanently( struct passwd *pw, char *homedir );
int get_security_context(const char *name,
int crontab_fd,
security_context_t *rcontext,
const char *tabname
);
void free_security_context( security_context_t *scontext );
int crontab_security_access(void);
/* PAM */
#ifdef WITH_PAM
int cron_start_pam(struct passwd *pw);
void cron_close_pam(void);
#endif
#endif /* CRONIE_FUNCS_H */

Some files were not shown because too many files have changed in this diff Show More