From 40afaea3488b0b7addf2f871323a72c4dcf8ca40 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Sat, 1 Jul 2023 12:15:55 +0200 Subject: [PATCH] Imported Upstream version 1.6.1 --- COPYING.obstack | 510 ++++++++++++++++++++++++++++++++++++++++ ChangeLog | 195 +++++++++++++++ Makefile.am | 2 + Makefile.in | 148 +++++++----- NEWS | 23 ++ aclocal.m4 | 48 ++-- anacron/Makemodule.am | 7 +- anacron/main.c | 8 +- anacron/readtab.c | 2 +- anacron/runjob.c | 33 ++- config.h.in | 6 - configure | 132 ++++++----- configure.ac | 20 +- contrib/0anacron | 8 +- cronie_common.c | 137 +++++++++++ cronie_common.h | 6 + man/anacrontab.5 | 17 ++ man/cron.8 | 3 + man/crontab.1 | 20 +- man/crontab.5 | 22 +- obstack/obstack.c | 376 +++++++++++++++++++++++++++++ obstack/obstack.h | 535 ++++++++++++++++++++++++++++++++++++++++++ src/Makemodule.am | 11 + src/cron.c | 25 +- src/crontab.c | 299 +++++++++++++---------- src/database.c | 17 +- src/do_command.c | 35 ++- src/entry.c | 290 ++++++++++++++--------- src/env.c | 2 +- src/job.c | 4 +- src/macros.h | 2 +- src/pathnames.h | 10 +- src/pw_dup.c | 1 + src/security.c | 2 +- 34 files changed, 2531 insertions(+), 425 deletions(-) create mode 100644 COPYING.obstack create mode 100644 cronie_common.c create mode 100644 obstack/obstack.c create mode 100644 obstack/obstack.h diff --git a/COPYING.obstack b/COPYING.obstack new file mode 100644 index 0000000..2d2d780 --- /dev/null +++ b/COPYING.obstack @@ -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. + + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/ChangeLog b/ChangeLog index a5a3fe4..1a465c0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,198 @@ +2022-03-23 w30023233 + + * Fix regression in handling 1-5 crontab entries + +2022-03-22 Tomas Mraz + + * Release version 1.6.0 + +2022-03-22 w30023233 + + * Add switch -f for foreground mode + +2022-03-22 Tomas Mraz + + * Fix regression in handling */x crontab entries + +2022-01-05 Tomas Mraz + + * get_number: Add missing NUL termination for the scanned string + +2021-11-08 Ondřej Pohořelský <35430604+opohorel@users.noreply.github.com> + + * Add random within range '~' operator + +2021-11-03 Nicolas Limare + + * Fix RandomScale calculation + +2021-10-27 sgerwk + + * include the case where runstate is undefined + +2021-10-26 sgerwk + + * use the configure runstatedir directory for pid file + +2021-09-02 Nils Philippsen + + * crond: Fix description of '-P' option + +2021-07-13 Danilo Spinella + + * Increase the maximum number of crontab entries + +2021-04-29 Jan Staněk + + * Address issues found by coverity scan + +2021-03-29 Christian Hesse + + * crontab: use bold colors + +2021-03-29 Tomas Mraz + + * Release new version 1.5.7 + +2021-03-29 Tomas Mraz + + * crond: Skip blanks between user name and command + +2021-03-29 Tomas Mraz + + * 0anacron: Check only Mains type power_supply for status + +2021-03-22 Tomas Mraz + + * 0anacron: Ignore Battery type power supply devices + +2021-03-19 Tomas Mraz + + * crontab: switch off colors if NO_COLOR is set + +2021-03-17 Lars Wendler + + * configure.ac: Don't use AM_CONDITIONAL inside an if statement + or else configure might break: + +2021-03-17 Tomas Mraz + + * Release new version 1.5.6 + +2021-03-17 Tomas Mraz + + * Partially revert the behavior of crontab command without arguments + If the stdin is not a TTY we behave as required in the POSIX spec. + This still prevents mistakes from simply running crontab without + arguments in a terminal. + +2021-03-17 Tomas Mraz + + * Any online power supply indicates that the system is on mains + +2021-01-22 Tomas Mraz + + * Fix some compilation warnings + +2021-01-22 Tomas Mraz + + * Always load database on startup even if all files have st_mtime == 0 + +2020-11-28 Mark Hills + + * Fix compiler warnings on 32-bit systems + +2020-12-28 Sami Kerola + + * build-sys: update to autoconf 2.70 + +2020-12-24 Hyunsoo Choe + + * Fix race condition between file update and load_database + Issue: https://github.com/cronie-crond/cronie/issues/73 + +2020-11-02 Tomas Mraz + + * configure.ac: Drop the bug report e-mail from AC_INIT() + Fixes #70 + +2020-10-25 Wayne Van Son + + * docs(readme): adds Void Linux to packaged distributions + +2020-08-20 Björn Persson + + * Add missing Content-Transfer-Encoding. + +2020-08-03 Kevin Adler + + * Fix anacron build when obstack not available + +2020-07-23 Fernando Cappi <3818944+fcappi@users.noreply.github.com> + + * Expand MAILTO and MAILFROM environment variables + +2020-07-13 J. Paul Reed + + * Fix crontab.5 man page, wrt verbiage regarding named lists/ranges for crontab entries. + See https://github.com/cronie-crond/cronie/issues/62 for more info. + +2020-06-11 Fernando Cappi <3818944+fcappi@users.noreply.github.com> + + * Add new option to test a crontab file syntax without installing it + Co-authored-by: Fernando Cappi + +2020-06-10 Tomas Mraz + + * Update packit configuration for latest Fedora versions + +2020-06-10 Tomas Mraz + + * Fix the .spec for packit + +2020-04-21 Tomas Mraz + + * Sync cronie.spec with Fedora + +2019-11-11 Dominika Hodovska + + * Enable copr builds and add packit config + +2020-04-21 Tomas Mraz + + * Handle out-of-memory condition from mkprints() call + +2020-04-20 John Horne + + * Ensure the command name is not null before logging it. + +2020-04-20 John Horne + + * Moved CMDEND code to outside for for loop. + +2020-04-20 John Horne + + * Add CMDEND log entry to log when cron jobs end. + +2020-03-02 Ian2020 + + * Allow backslashes when not part of a line continuation + +2019-12-16 Tomas Mraz + + * Fix incorrect flag set for @weekly jobs. + Fixes #52 + +2019-11-24 Josef Schlehofer + + * Fix incorrect include of fcntl.h + +2019-11-06 Tomas Mraz + + * crontab: Fix coloring when crontab ends with comment + Also emphasize a missing EOL at EOF by red warning text + Fixes #45 + 2019-10-31 Tomas Mraz * Release new version 1.5.5 diff --git a/Makefile.am b/Makefile.am index 8b95e6e..4b49d8b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -13,6 +13,8 @@ dist_noinst_HEADERS = \ EXTRA_DIST += \ README.anacron \ COPYING.anacron \ + COPYING.obstack \ + obstack/obstack.h \ cronie.init \ crond.sysconfig \ contrib/anacrontab \ diff --git a/Makefile.in b/Makefile.in index 50e1892..20a2edc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.1 from Makefile.am. +# Makefile.in generated by automake 1.16.2 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2018 Free Software Foundation, Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -100,12 +100,13 @@ sbin_PROGRAMS = $(am__EXEEXT_1) src/crond$(EXEEXT) @ANACRON_TRUE@am__append_2 = anacron/anacron @ANACRON_TRUE@am__append_3 = anacron-paths.h @ANACRON_TRUE@am__append_4 = $(common_nodist) +@ANACRON_TRUE@@NEED_OBSTACK_TRUE@am__append_5 = obstack/obstack.c # 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. -@ANACRON_TRUE@am__append_5 = anacron-paths.h -@ANACRON_TRUE@am__append_6 = $(anacron_man) +@ANACRON_TRUE@am__append_6 = anacron-paths.h +@ANACRON_TRUE@am__append_7 = $(anacron_man) subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac @@ -128,41 +129,46 @@ PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS) am__anacron_anacron_SOURCES_DIST = 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 + anacron/readtab.c anacron/runjob.c cronie_common.c \ + obstack/obstack.c am__dirstamp = $(am__leading_dot)dirstamp +@ANACRON_TRUE@@NEED_OBSTACK_TRUE@am__objects_1 = \ +@ANACRON_TRUE@@NEED_OBSTACK_TRUE@ obstack/obstack.$(OBJEXT) @ANACRON_TRUE@am_anacron_anacron_OBJECTS = anacron/gregor.$(OBJEXT) \ @ANACRON_TRUE@ anacron/lock.$(OBJEXT) anacron/log.$(OBJEXT) \ @ANACRON_TRUE@ anacron/main.$(OBJEXT) anacron/matchrx.$(OBJEXT) \ @ANACRON_TRUE@ anacron/readtab.$(OBJEXT) \ -@ANACRON_TRUE@ anacron/runjob.$(OBJEXT) -am__objects_1 = -am__objects_2 = $(am__objects_1) -@ANACRON_TRUE@nodist_anacron_anacron_OBJECTS = $(am__objects_2) +@ANACRON_TRUE@ anacron/runjob.$(OBJEXT) cronie_common.$(OBJEXT) \ +@ANACRON_TRUE@ $(am__objects_1) +am__objects_2 = +am__objects_3 = $(am__objects_2) +@ANACRON_TRUE@nodist_anacron_anacron_OBJECTS = $(am__objects_3) anacron_anacron_OBJECTS = $(am_anacron_anacron_OBJECTS) \ $(nodist_anacron_anacron_OBJECTS) am__DEPENDENCIES_1 = @ANACRON_TRUE@anacron_anacron_DEPENDENCIES = $(am__DEPENDENCIES_1) \ @ANACRON_TRUE@ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -am__objects_3 = src/entry.$(OBJEXT) src/env.$(OBJEXT) \ +am__objects_4 = src/entry.$(OBJEXT) src/env.$(OBJEXT) \ src/misc.$(OBJEXT) src/pw_dup.$(OBJEXT) am_src_crond_OBJECTS = src/cron.$(OBJEXT) src/database.$(OBJEXT) \ src/do_command.$(OBJEXT) src/job.$(OBJEXT) src/popen.$(OBJEXT) \ - src/security.$(OBJEXT) src/user.$(OBJEXT) $(am__objects_3) -nodist_src_crond_OBJECTS = $(am__objects_2) + src/security.$(OBJEXT) src/user.$(OBJEXT) \ + cronie_common.$(OBJEXT) $(am__objects_4) +nodist_src_crond_OBJECTS = $(am__objects_3) src_crond_OBJECTS = $(am_src_crond_OBJECTS) \ $(nodist_src_crond_OBJECTS) src_crond_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) am_src_cronnext_OBJECTS = src/cronnext.$(OBJEXT) \ src/database.$(OBJEXT) src/job.$(OBJEXT) src/user.$(OBJEXT) \ - $(am__objects_3) -nodist_src_cronnext_OBJECTS = $(am__objects_2) + $(am__objects_4) +nodist_src_cronnext_OBJECTS = $(am__objects_3) src_cronnext_OBJECTS = $(am_src_cronnext_OBJECTS) \ $(nodist_src_cronnext_OBJECTS) src_cronnext_LDADD = $(LDADD) am_src_crontab_OBJECTS = src/crontab.$(OBJEXT) src/security.$(OBJEXT) \ - $(am__objects_3) -nodist_src_crontab_OBJECTS = $(am__objects_2) + $(am__objects_4) +nodist_src_crontab_OBJECTS = $(am__objects_3) src_crontab_OBJECTS = $(am_src_crontab_OBJECTS) \ $(nodist_src_crontab_OBJECTS) src_crontab_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @@ -182,10 +188,11 @@ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles -am__depfiles_remade = anacron/$(DEPDIR)/gregor.Po \ - anacron/$(DEPDIR)/lock.Po anacron/$(DEPDIR)/log.Po \ - anacron/$(DEPDIR)/main.Po anacron/$(DEPDIR)/matchrx.Po \ - anacron/$(DEPDIR)/readtab.Po anacron/$(DEPDIR)/runjob.Po \ +am__depfiles_remade = ./$(DEPDIR)/cronie_common.Po \ + anacron/$(DEPDIR)/gregor.Po anacron/$(DEPDIR)/lock.Po \ + anacron/$(DEPDIR)/log.Po anacron/$(DEPDIR)/main.Po \ + anacron/$(DEPDIR)/matchrx.Po anacron/$(DEPDIR)/readtab.Po \ + anacron/$(DEPDIR)/runjob.Po obstack/$(DEPDIR)/obstack.Po \ src/$(DEPDIR)/cron.Po src/$(DEPDIR)/cronnext.Po \ src/$(DEPDIR)/crontab.Po src/$(DEPDIR)/database.Po \ src/$(DEPDIR)/do_command.Po src/$(DEPDIR)/entry.Po \ @@ -253,8 +260,8 @@ MANS = $(dist_man_MANS) am__dist_pam_DATA_DIST = pam/crond DATA = $(dist_pam_DATA) HEADERS = $(dist_noinst_HEADERS) -am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ - $(LISP)config.h.in +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ + config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. @@ -396,6 +403,7 @@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ @@ -411,10 +419,11 @@ BUILT_SOURCES = $(am__append_4) $(common_nodist) # 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 = $(am__append_5) cron-paths.h -EXTRA_DIST = README.anacron COPYING.anacron cronie.init \ - crond.sysconfig contrib/anacrontab contrib/0anacron \ - contrib/0hourly contrib/dailyjobs contrib/cronie.systemd \ +CLEANFILES = $(am__append_6) cron-paths.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 $(am__append_1) $(anacron_man) common_nodist = $(am__append_3) cron-paths.h dist_noinst_HEADERS = \ @@ -422,23 +431,16 @@ dist_noinst_HEADERS = \ @PAM_TRUE@pamdir = $(sysconfdir)/pam.d @PAM_TRUE@dist_pam_DATA = pam/crond -@ANACRON_TRUE@anacron_anacron_SOURCES = \ -@ANACRON_TRUE@ anacron-paths.h \ -@ANACRON_TRUE@ anacron/global.h \ -@ANACRON_TRUE@ anacron/gregor.c \ -@ANACRON_TRUE@ anacron/gregor.h \ -@ANACRON_TRUE@ anacron/lock.c \ -@ANACRON_TRUE@ anacron/log.c \ -@ANACRON_TRUE@ anacron/main.c \ -@ANACRON_TRUE@ anacron/matchrx.c \ -@ANACRON_TRUE@ anacron/matchrx.h \ -@ANACRON_TRUE@ anacron/readtab.c \ -@ANACRON_TRUE@ anacron/runjob.c - +@ANACRON_TRUE@anacron_anacron_SOURCES = anacron-paths.h \ +@ANACRON_TRUE@ anacron/global.h anacron/gregor.c \ +@ANACRON_TRUE@ anacron/gregor.h anacron/lock.c anacron/log.c \ +@ANACRON_TRUE@ anacron/main.c anacron/matchrx.c \ +@ANACRON_TRUE@ anacron/matchrx.h anacron/readtab.c \ +@ANACRON_TRUE@ anacron/runjob.c cronie_common.c $(am__append_5) @ANACRON_TRUE@nodist_anacron_anacron_SOURCES = $(common_nodist) @ANACRON_TRUE@anacron_anacron_LDADD = $(LIBSELINUX) $(LIBPAM) $(LIBAUDIT) dist_man_MANS = man/cron.8 man/crond.8 man/cronnext.1 man/crontab.1 \ - man/crontab.5 $(am__append_6) + man/crontab.5 $(am__append_7) anacron_man = \ man/anacrontab.5 \ man/anacron.8 @@ -451,6 +453,7 @@ src_crond_SOURCES = \ src/popen.c \ src/security.c \ src/user.c \ + cronie_common.c \ $(common_src) src_crontab_SOURCES = \ @@ -483,6 +486,10 @@ nodist_src_crontab_SOURCES = $(common_nodist) nodist_src_cronnext_SOURCES = $(common_nodist) src_crond_LDADD = $(LIBSELINUX) $(LIBPAM) $(LIBAUDIT) src_crontab_LDADD = $(LIBSELINUX) $(LIBPAM) $(LIBAUDIT) +@HAS_RUNSTATE_FALSE@cronpidcomment = +@HAS_RUNSTATE_TRUE@cronpidcomment = /* directory of cron pid file */ +@HAS_RUNSTATE_FALSE@cronpiddir = +@HAS_RUNSTATE_TRUE@cronpiddir = \#define CRON_PID_DIR "$(runstatedir)" all: $(BUILT_SOURCES) config.h $(MAKE) $(AM_MAKEFLAGS) all-am @@ -641,6 +648,14 @@ anacron/readtab.$(OBJEXT): anacron/$(am__dirstamp) \ anacron/$(DEPDIR)/$(am__dirstamp) anacron/runjob.$(OBJEXT): anacron/$(am__dirstamp) \ anacron/$(DEPDIR)/$(am__dirstamp) +obstack/$(am__dirstamp): + @$(MKDIR_P) obstack + @: > obstack/$(am__dirstamp) +obstack/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) obstack/$(DEPDIR) + @: > obstack/$(DEPDIR)/$(am__dirstamp) +obstack/obstack.$(OBJEXT): obstack/$(am__dirstamp) \ + obstack/$(DEPDIR)/$(am__dirstamp) anacron/anacron$(EXEEXT): $(anacron_anacron_OBJECTS) $(anacron_anacron_DEPENDENCIES) $(EXTRA_anacron_anacron_DEPENDENCIES) anacron/$(am__dirstamp) @rm -f anacron/anacron$(EXEEXT) @@ -686,11 +701,13 @@ src/crontab$(EXEEXT): $(src_crontab_OBJECTS) $(src_crontab_DEPENDENCIES) $(EXTRA mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f anacron/*.$(OBJEXT) + -rm -f obstack/*.$(OBJEXT) -rm -f src/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cronie_common.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@anacron/$(DEPDIR)/gregor.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@anacron/$(DEPDIR)/lock.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@anacron/$(DEPDIR)/log.Po@am__quote@ # am--include-marker @@ -698,6 +715,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@anacron/$(DEPDIR)/matchrx.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@anacron/$(DEPDIR)/readtab.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@anacron/$(DEPDIR)/runjob.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@obstack/$(DEPDIR)/obstack.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/cron.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/cronnext.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/$(DEPDIR)/crontab.Po@am__quote@ # am--include-marker @@ -1001,6 +1019,10 @@ dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) +dist-zstd: distdir + tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst + $(am__post_remove_distdir) + dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @@ -1043,6 +1065,8 @@ distcheck: dist eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ + *.tar.zst*) \ + zstd -dc $(distdir).tar.zst | $(am__untar) ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) @@ -1147,6 +1171,8 @@ distclean-generic: -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f anacron/$(DEPDIR)/$(am__dirstamp) -rm -f anacron/$(am__dirstamp) + -rm -f obstack/$(DEPDIR)/$(am__dirstamp) + -rm -f obstack/$(am__dirstamp) -rm -f src/$(DEPDIR)/$(am__dirstamp) -rm -f src/$(am__dirstamp) @@ -1161,13 +1187,15 @@ clean-am: clean-binPROGRAMS clean-generic clean-sbinPROGRAMS \ distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -f anacron/$(DEPDIR)/gregor.Po + -rm -f ./$(DEPDIR)/cronie_common.Po + -rm -f anacron/$(DEPDIR)/gregor.Po -rm -f anacron/$(DEPDIR)/lock.Po -rm -f anacron/$(DEPDIR)/log.Po -rm -f anacron/$(DEPDIR)/main.Po -rm -f anacron/$(DEPDIR)/matchrx.Po -rm -f anacron/$(DEPDIR)/readtab.Po -rm -f anacron/$(DEPDIR)/runjob.Po + -rm -f obstack/$(DEPDIR)/obstack.Po -rm -f src/$(DEPDIR)/cron.Po -rm -f src/$(DEPDIR)/cronnext.Po -rm -f src/$(DEPDIR)/crontab.Po @@ -1228,13 +1256,15 @@ installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache - -rm -f anacron/$(DEPDIR)/gregor.Po + -rm -f ./$(DEPDIR)/cronie_common.Po + -rm -f anacron/$(DEPDIR)/gregor.Po -rm -f anacron/$(DEPDIR)/lock.Po -rm -f anacron/$(DEPDIR)/log.Po -rm -f anacron/$(DEPDIR)/main.Po -rm -f anacron/$(DEPDIR)/matchrx.Po -rm -f anacron/$(DEPDIR)/readtab.Po -rm -f anacron/$(DEPDIR)/runjob.Po + -rm -f obstack/$(DEPDIR)/obstack.Po -rm -f src/$(DEPDIR)/cron.Po -rm -f src/$(DEPDIR)/cronnext.Po -rm -f src/$(DEPDIR)/crontab.Po @@ -1274,21 +1304,22 @@ uninstall-man: uninstall-man1 uninstall-man5 uninstall-man8 check-am clean clean-binPROGRAMS clean-cscope clean-generic \ clean-sbinPROGRAMS cscope cscopelist-am ctags ctags-am dist \ dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \ - dist-xz dist-zip distcheck distclean distclean-compile \ - distclean-generic distclean-hdr distclean-tags distcleancheck \ - distdir distuninstallcheck dvi dvi-am html html-am info \ - info-am install install-am install-binPROGRAMS install-data \ - install-data-am install-dist_pamDATA install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-man1 install-man5 install-man8 install-pdf \ - install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ - tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ - uninstall-dist_pamDATA uninstall-man uninstall-man1 \ - uninstall-man5 uninstall-man8 uninstall-sbinPROGRAMS + dist-xz dist-zip dist-zstd distcheck distclean \ + distclean-compile distclean-generic distclean-hdr \ + distclean-tags distcleancheck distdir distuninstallcheck dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am \ + install-dist_pamDATA install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-man1 install-man5 \ + install-man8 install-pdf install-pdf-am install-ps \ + install-ps-am install-sbinPROGRAMS install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am uninstall-binPROGRAMS uninstall-dist_pamDATA \ + uninstall-man uninstall-man1 uninstall-man5 uninstall-man8 \ + uninstall-sbinPROGRAMS .PRECIOUS: Makefile @@ -1337,6 +1368,9 @@ cron-paths.h: Makefile */ \ #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)" \ diff --git a/NEWS b/NEWS index ffc07ce..e9b84d3 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,28 @@ 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 diff --git a/aclocal.m4 b/aclocal.m4 index 8c6b78f..e4b15fc 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.16.1 -*- Autoconf -*- +# generated automatically by aclocal 1.16.2 -*- Autoconf -*- -# Copyright (C) 1996-2018 Free Software Foundation, Inc. +# Copyright (C) 1996-2020 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -20,7 +20,7 @@ You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) -# Copyright (C) 2002-2018 Free Software Foundation, Inc. +# Copyright (C) 2002-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -35,7 +35,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.16.1], [], +m4_if([$1], [1.16.2], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -51,14 +51,14 @@ m4_define([_AM_AUTOCONF_VERSION], []) # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.16.1])dnl +[AM_AUTOMAKE_VERSION([1.16.2])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -110,7 +110,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd` # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2018 Free Software Foundation, Inc. +# Copyright (C) 1997-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -141,7 +141,7 @@ AC_CONFIG_COMMANDS_PRE( Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2018 Free Software Foundation, Inc. +# Copyright (C) 1999-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -332,7 +332,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2018 Free Software Foundation, Inc. +# Copyright (C) 1999-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -371,7 +371,9 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments - for automatic dependency tracking. Try re-running configure with the + for automatic dependency tracking. If GNU make was not used, consider + re-running the configure script with MAKE="gmake" (or whatever is + necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi @@ -398,7 +400,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2018 Free Software Foundation, Inc. +# Copyright (C) 1996-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -595,7 +597,7 @@ for _am_header in $config_headers :; do done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -616,7 +618,7 @@ if test x"${install_sh+set}" != xset; then fi AC_SUBST([install_sh])]) -# Copyright (C) 2003-2018 Free Software Foundation, Inc. +# Copyright (C) 2003-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -637,7 +639,7 @@ AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -680,7 +682,7 @@ AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2018 Free Software Foundation, Inc. +# Copyright (C) 1997-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -719,7 +721,7 @@ fi # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -748,7 +750,7 @@ AC_DEFUN([_AM_SET_OPTIONS], AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2018 Free Software Foundation, Inc. +# Copyright (C) 1999-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -795,7 +797,7 @@ AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -814,7 +816,7 @@ AC_DEFUN([AM_RUN_LOG], # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2018 Free Software Foundation, Inc. +# Copyright (C) 1996-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -895,7 +897,7 @@ AC_CONFIG_COMMANDS_PRE( rm -f conftest.file ]) -# Copyright (C) 2009-2018 Free Software Foundation, Inc. +# Copyright (C) 2009-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -955,7 +957,7 @@ AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2018 Free Software Foundation, Inc. +# Copyright (C) 2001-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -983,7 +985,7 @@ fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2018 Free Software Foundation, Inc. +# Copyright (C) 2006-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1002,7 +1004,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2018 Free Software Foundation, Inc. +# Copyright (C) 2004-2020 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff --git a/anacron/Makemodule.am b/anacron/Makemodule.am index 394640f..a01632e 100644 --- a/anacron/Makemodule.am +++ b/anacron/Makemodule.am @@ -12,11 +12,16 @@ anacron_anacron_SOURCES = \ anacron/matchrx.c \ anacron/matchrx.h \ anacron/readtab.c \ - anacron/runjob.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. diff --git a/anacron/main.c b/anacron/main.c index d092970..65f8fed 100644 --- a/anacron/main.c +++ b/anacron/main.c @@ -44,8 +44,8 @@ int day_now; int year, month, day_of_month; /* date anacron started */ char *program_name; -char *anacrontab; -char *spooldir; +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 */ @@ -128,12 +128,14 @@ parse_opts(int argc, char *argv[]) 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': @@ -208,9 +210,11 @@ go_background(void) /* 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(); diff --git a/anacron/readtab.c b/anacron/readtab.c index a3f0220..acfd0cc 100644 --- a/anacron/readtab.c +++ b/anacron/readtab.c @@ -98,7 +98,7 @@ Return NULL if no more lines. break; } - if ('\\' != prev && 0 != prev && '\n' != prev) obstack_1grow(&input_o, (char)prev); + if (('\\' != prev || c != '\n') && 0 != prev && '\n' != prev) obstack_1grow(&input_o, (char)prev); else if ('\n' == prev) obstack_1grow(&input_o, ' '); prev = c; diff --git a/anacron/runjob.c b/anacron/runjob.c index a677463..04d6904 100644 --- a/anacron/runjob.c +++ b/anacron/runjob.c @@ -35,6 +35,7 @@ #include #include #include "global.h" +#include "cronie_common.h" #include @@ -236,7 +237,9 @@ launch_mailer(job_rec *jr) 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); @@ -286,6 +289,8 @@ launch_job(job_rec *jr) char hostname[512]; char *mailto; char *mailfrom; + char mailto_expanded[MAX_EMAILSTR]; + char mailfrom_expanded[MAX_EMAILSTR]; /* get hostname */ if (gethostname(hostname, 512)) { @@ -296,14 +301,31 @@ launch_job(job_rec *jr) /* Get the destination email address if set, or current user otherwise */ mailto = getenv("MAILTO"); - - if (mailto == NULL) - mailto = username(); + 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(); + 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; @@ -319,6 +341,7 @@ launch_job(job_rec *jr) 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 "); diff --git a/config.h.in b/config.h.in index 152c04a..33fb0d7 100644 --- a/config.h.in +++ b/config.h.in @@ -108,9 +108,6 @@ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_CDEFS_H -/* Define to 1 if you have the header file. */ -#undef HAVE_SYS_FCNTL_H - /* Define to 1 if you have the header file. */ #undef HAVE_SYS_INOTIFY_H @@ -174,9 +171,6 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION -/* Define as the return type of signal handlers (`int' or `void'). */ -#undef RETSIGTYPE - /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS diff --git a/configure b/configure index c80e556..39850f3 100755 --- a/configure +++ b/configure @@ -1,8 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for cronie 1.5.5. -# -# Report bugs to . +# Generated by GNU Autoconf 2.69 for cronie 1.6.1. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -266,9 +264,8 @@ fi $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else - $as_echo "$0: Please tell bug-autoconf@gnu.org and -$0: mmaslano@redhat.com,tmraz@fedoraproject.org about your -$0: system, including any error possibly output before this + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi @@ -580,9 +577,9 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='cronie' PACKAGE_TARNAME='cronie' -PACKAGE_VERSION='1.5.5' -PACKAGE_STRING='cronie 1.5.5' -PACKAGE_BUGREPORT='mmaslano@redhat.com,tmraz@fedoraproject.org' +PACKAGE_VERSION='1.6.1' +PACKAGE_STRING='cronie 1.6.1' +PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. @@ -625,6 +622,10 @@ ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS +HAS_RUNSTATE_FALSE +HAS_RUNSTATE_TRUE +NEED_OBSTACK_FALSE +NEED_OBSTACK_TRUE ANACRONTAB ANACRON_SPOOL_DIR ANACRON_FALSE @@ -714,6 +715,7 @@ infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -805,6 +807,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1057,6 +1060,15 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1194,7 +1206,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1307,7 +1319,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures cronie 1.5.5 to adapt to many kinds of systems. +\`configure' configures cronie 1.6.1 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1347,6 +1359,7 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -1377,7 +1390,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of cronie 1.5.5:";; + short | recursive ) echo "Configuration of cronie 1.6.1:";; esac cat <<\_ACEOF @@ -1434,7 +1447,7 @@ Some influential environment variables: Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. -Report bugs to . +Report bugs to the package provider. _ACEOF ac_status=$? fi @@ -1497,7 +1510,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -cronie configure 1.5.5 +cronie configure 1.6.1 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1656,10 +1669,6 @@ $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} -( $as_echo "## tttttttttttttttttttttttttttttttttt ## -## Report this to mmaslano@redhat.com ## -## tttttttttttttttttttttttttttttttttt ##" - ) | sed "s/^/$as_me: WARNING: /" >&2 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 @@ -1977,7 +1986,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by cronie $as_me 1.5.5, which was +It was created by cronie $as_me 1.6.1, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2844,7 +2853,7 @@ fi # Define the identity of the package. PACKAGE='cronie' - VERSION='1.5.5' + VERSION='1.6.1' cat >>confdefs.h <<_ACEOF @@ -4583,7 +4592,6 @@ for ac_header in \ sys/timers.h \ sys/types.h \ sys/cdefs.h \ - sys/fcntl.h \ time.h \ unistd.h \ util.h \ @@ -4702,39 +4710,6 @@ $as_echo "#define const /**/" >>confdefs.h fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5 -$as_echo_n "checking return type of signal handlers... " >&6; } -if ${ac_cv_type_signal+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include - -int -main () -{ -return *(signal (0, 0)) (0) == 1; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_type_signal=int -else - ac_cv_type_signal=void -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_signal" >&5 -$as_echo "$ac_cv_type_signal" >&6; } - -cat >>confdefs.h <<_ACEOF -#define RETSIGTYPE $ac_cv_type_signal -_ACEOF - - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5 $as_echo_n "checking for uid_t in sys/types.h... " >&6; } if ${ac_cv_type_uid_t+:} false; then : @@ -5423,8 +5398,39 @@ if test "$ANACRONTAB" = ""; then ANACRONTAB='${sysconfdir}/anacrontab' fi + + ac_fn_c_check_header_mongrel "$LINENO" "obstack.h" "ac_cv_header_obstack_h" "$ac_includes_default" +if test "x$ac_cv_header_obstack_h" = xyes; then : + have_obstack=yes +else + have_obstack=no fi + +fi + if test "$have_obstack" = no; then + NEED_OBSTACK_TRUE= + NEED_OBSTACK_FALSE='#' +else + NEED_OBSTACK_TRUE='#' + NEED_OBSTACK_FALSE= +fi + +if test "$enable_anacron" != no && test "$have_obstack" = no; then : + + CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/obstack" + +fi + + if test x$runstatedir != x; then + HAS_RUNSTATE_TRUE= + HAS_RUNSTATE_FALSE='#' +else + HAS_RUNSTATE_TRUE='#' + HAS_RUNSTATE_FALSE= +fi + + ac_config_files="$ac_config_files Makefile" cat >confcache <<\_ACEOF @@ -5568,6 +5574,14 @@ if test -z "${ANACRON_TRUE}" && test -z "${ANACRON_FALSE}"; then as_fn_error $? "conditional \"ANACRON\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${NEED_OBSTACK_TRUE}" && test -z "${NEED_OBSTACK_FALSE}"; then + as_fn_error $? "conditional \"NEED_OBSTACK\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAS_RUNSTATE_TRUE}" && test -z "${HAS_RUNSTATE_FALSE}"; then + as_fn_error $? "conditional \"HAS_RUNSTATE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 @@ -5965,7 +5979,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by cronie $as_me 1.5.5, which was +This file was extended by cronie $as_me 1.6.1, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6025,13 +6039,13 @@ $config_headers Configuration commands: $config_commands -Report bugs to ." +Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -cronie config.status 1.5.5 +cronie config.status 1.6.1 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -6840,7 +6854,9 @@ $as_echo X/"$am_mf" | { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments - for automatic dependency tracking. Try re-running configure with the + for automatic dependency tracking. If GNU make was not used, consider + re-running the configure script with MAKE=\"gmake\" (or whatever is + necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See \`config.log' for more details" "$LINENO" 5; } diff --git a/configure.ac b/configure.ac index bffe9ad..e37f0ca 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ -AC_INIT([cronie],[1.5.5],[mmaslano@redhat.com,tmraz@fedoraproject.org]) -AC_CONFIG_HEADER([config.h]) -AC_PREREQ(2.60) +AC_INIT([cronie],[1.6.1]) +AC_CONFIG_HEADERS([config.h]) +AC_PREREQ([2.64]) AM_INIT_AUTOMAKE([subdir-objects]) @@ -38,7 +38,6 @@ AC_CHECK_HEADERS( \ sys/timers.h \ sys/types.h \ sys/cdefs.h \ - sys/fcntl.h \ time.h \ unistd.h \ util.h \ @@ -55,7 +54,6 @@ AC_CHECK_FUNCS( \ dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST -AC_TYPE_SIGNAL AC_TYPE_UID_T AC_TYPE_MODE_T AC_TYPE_OFF_T @@ -67,7 +65,7 @@ AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[#include ]) dnl Checking for programs AC_ARG_WITH([editor], - [AC_HELP_STRING([--with-editor=EDITOR], [path to default 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"], [ @@ -258,7 +256,17 @@ 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 diff --git a/contrib/0anacron b/contrib/0anacron index 8fc9be1..c2b0302 100644 --- a/contrib/0anacron +++ b/contrib/0anacron @@ -9,11 +9,9 @@ fi # Do not run jobs when on battery power online=1 -for psupply in AC ADP{0..9} ; do - sysfile="/sys/class/power_supply/$psupply/online" - - if [ -f $sysfile ] ; then - if [ `cat $sysfile 2>/dev/null`x = 1x ]; then +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 diff --git a/cronie_common.c b/cronie_common.c new file mode 100644 index 0000000..ea98f46 --- /dev/null +++ b/cronie_common.c @@ -0,0 +1,137 @@ +#include +#include +#include + +/* 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; +} diff --git a/cronie_common.h b/cronie_common.h index 4ce172e..45d2fb9 100644 --- a/cronie_common.h +++ b/cronie_common.h @@ -34,4 +34,10 @@ # 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 */ diff --git a/man/anacrontab.5 b/man/anacrontab.5 index c016b4e..507cfd8 100644 --- a/man/anacrontab.5 +++ b/man/anacrontab.5 @@ -65,6 +65,23 @@ 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. diff --git a/man/cron.8 b/man/cron.8 index 435f7cc..e756477 100644 --- a/man/cron.8 +++ b/man/cron.8 @@ -169,6 +169,9 @@ 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 diff --git a/man/crontab.1 b/man/crontab.1 index 47b0b8f..d080cef 100644 --- a/man/crontab.1 +++ b/man/crontab.1 @@ -33,6 +33,11 @@ crontab \- maintains crontab files for individual users .RB | \ - > .br .B crontab +.RB [ -T ] +.RI < "file" +.RB | \ - > +.br +.B crontab .RB [ -u .IR user ] .RB < -l " | " -r " | " -e >\ [ -i ] @@ -109,6 +114,11 @@ 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" @@ -121,6 +131,10 @@ 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 @@ -221,9 +235,9 @@ The 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. This new command -syntax differs from previous versions of Vixie Cron, as well as from the -classic SVR3 syntax. +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. diff --git a/man/crontab.5 b/man/crontab.5 index fe716fc..5d89862 100644 --- a/man/crontab.5 +++ b/man/crontab.5 @@ -99,6 +99,12 @@ 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' @@ -199,6 +205,15 @@ 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 @@ -212,7 +227,8 @@ 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 or lists of names are not allowed. +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 @@ -235,6 +251,10 @@ 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 diff --git a/obstack/obstack.c b/obstack/obstack.c new file mode 100644 index 0000000..0a4e57e --- /dev/null +++ b/obstack/obstack.c @@ -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 + . */ + + +#ifdef _LIBC +# include +#else +# include +# 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 +# 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 struct alignof_helper { char __slot1; type __slot2; }; +# define __alignof__(type) offsetof (alignof_helper, __slot2) +# else +# define __alignof__(type) \ + offsetof (struct { char __slot1; type __slot2; }, __slot2) +# endif +# endif +# include +# include + +# 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 + +/* 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 +# 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 +# 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 */ diff --git a/obstack/obstack.h b/obstack/obstack.h new file mode 100644 index 0000000..43c71f4 --- /dev/null +++ b/obstack/obstack.h @@ -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 + . */ + +/* 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 /* For size_t and ptrdiff_t. */ +#include /* 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 */ diff --git a/src/Makemodule.am b/src/Makemodule.am index b531d25..1744c6a 100644 --- a/src/Makemodule.am +++ b/src/Makemodule.am @@ -15,6 +15,7 @@ src_crond_SOURCES = \ src/popen.c \ src/security.c \ src/user.c \ + cronie_common.c \ $(common_src) src_crontab_SOURCES = \ @@ -60,6 +61,13 @@ src_crontab_LDADD = $(LIBSELINUX) $(LIBPAM) $(LIBAUDIT) # 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 #\ @@ -94,6 +102,9 @@ cron-paths.h: Makefile */ \ #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)" \ diff --git a/src/cron.c b/src/cron.c index 7eabfe9..4297fc4 100644 --- a/src/cron.c +++ b/src/cron.c @@ -40,15 +40,12 @@ #include #include #include +#include #ifdef WITH_INOTIFY # include #endif -#ifdef HAVE_SYS_FCNTL_H -# include -#endif - #include "cronie_common.h" #include "funcs.h" #include "globals.h" @@ -185,8 +182,10 @@ static void usage(void) { fprintf(stderr, " -i deamon runs without inotify support\n"); fprintf(stderr, " -m 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 use PATH=\"%s\"\n", _PATH_DEFPATH); + 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"); @@ -310,16 +309,11 @@ int main(int argc, char *argv[]) { if (gettimeofday(&tv, &tz) != 0) tv.tv_usec = 0; srandom((unsigned int)(pid + tv.tv_usec)); - RandomScale = (double)random() / (double)RAND_MAX; + 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); - database.head = NULL; - database.tail = NULL; - database.mtime = (time_t) 0; - - load_database(&database); fd = -1; #if defined WITH_INOTIFY @@ -337,6 +331,12 @@ int main(int argc, char *argv[]) { } #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; @@ -691,13 +691,14 @@ static void sigchld_reaper(void) { static void parse_args(int argc, char *argv[]) { int argch; - while (-1 != (argch = getopt(argc, argv, "hnpsiPx:m:cV"))) { + 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': diff --git a/src/crontab.c b/src/crontab.c index 68f31f0..41c8984 100644 --- a/src/crontab.c +++ b/src/crontab.c @@ -66,24 +66,30 @@ #define NHEADER_LINES 0 -#define COMMENT_COLOR "\x1B[34m" -#define RESET_COLOR "\033[0m" +#define COMMENT_COLOR "\x1B[34;1m" +#define ERROR_COLOR "\x1B[31;1m" +#define RESET_COLOR "\x1B[0m" -enum opt_t {opt_unknown, opt_list, opt_delete, opt_edit, opt_replace, opt_hostset, opt_hostget}; +enum opt_t { + opt_unknown, opt_list, opt_delete, opt_edit, opt_replace, opt_hostset, + opt_hostget, opt_test +}; #if DEBUGGING -static const char *Options[] = {"???", "list", "delete", "edit", "replace", "hostset", "hostget"}; +static const char *Options[] = { + "???", "list", "delete", "edit", "replace", "hostset", "hostget", "test" +}; # ifdef WITH_SELINUX -static const char *getoptargs = "u:lerisncx:V"; +static const char *getoptargs = "u:lerisncx:VT"; # else -static const char *getoptargs = "u:lerincx:V"; +static const char *getoptargs = "u:lerincx:VT"; # endif #else # ifdef WITH_SELINUX -static const char *getoptargs = "u:lerisncV"; +static const char *getoptargs = "u:lerisncVT"; # else -static const char *getoptargs = "u:lerincV"; +static const char *getoptargs = "u:lerincVT"; # endif #endif #ifdef WITH_SELINUX @@ -104,9 +110,12 @@ static void list_cmd(void), delete_cmd(void), edit_cmd(void), poke_daemon(void), -check_error(const char *), parse_args(int c, char *v[]), die(int) ATTRIBUTE_NORETURN; -static int replace_cmd(void), hostset_cmd(void), hostget_cmd(void); -static char *host_specific_filename(const char *prefix, const char *suffix); +check_error(const char *), parse_args(int c, char *v[]), +die(int) ATTRIBUTE_NORETURN; +static int replace_cmd(void), hostset_cmd(void), hostget_cmd(void), +test_cmd(void), check_syntax(FILE *); +static char *host_specific_filename(const char *prefix, +const char *suffix); static const char *tmp_path(void); static void usage(const char *msg) ATTRIBUTE_NORETURN; @@ -125,6 +134,7 @@ static void usage(const char *msg) { fprintf(stderr, " -i prompt before deleting\n"); fprintf(stderr, " -n set host in cluster to run users' crontabs\n"); fprintf(stderr, " -c get host in cluster to run users' crontabs\n"); + fprintf(stderr, " -T test a crontab file syntax\n"); #ifdef WITH_SELINUX fprintf(stderr, " -s selinux context\n"); #endif @@ -139,7 +149,7 @@ static void usage(const char *msg) { int main(int argc, char *argv[]) { int exitstatus; - if ((ProgramName=strrchr(argv[0], '/')) == NULL) { + if ((ProgramName = strrchr(argv[0], '/')) == NULL) { ProgramName = argv[0]; } else { @@ -201,6 +211,10 @@ int main(int argc, char *argv[]) { if (hostget_cmd() < 0) exitstatus = ERROR_EXIT; break; + case opt_test: + if (test_cmd() < 0) + exitstatus = ERROR_EXIT; + break; default: abort(); } @@ -249,9 +263,9 @@ static void parse_args(int argc, char *argv[]) { exit(ERROR_EXIT); } #endif - if (Option == opt_hostset || Option == opt_hostget) { - fprintf(stderr, - "cannot use -u with -n or -c\n"); + if (Option == opt_hostset || Option == opt_hostget || + Option == opt_test) { + fprintf(stderr, "cannot use -u with -n, -c or -T\n"); exit(ERROR_EXIT); } @@ -279,6 +293,11 @@ static void parse_args(int argc, char *argv[]) { usage("only one operation permitted"); Option = opt_edit; break; + case 'T': + if (Option != opt_unknown) + usage("only one operation permitted"); + Option = opt_test; + break; case 'i': PromptOnDelete = 1; break; @@ -292,15 +311,13 @@ static void parse_args(int argc, char *argv[]) { #endif case 'n': if (MY_UID(pw) != ROOT_UID) { - fprintf(stderr, - "must be privileged to set host with -n\n"); + fprintf(stderr, "must be privileged to set host with -n\n"); exit(ERROR_EXIT); } if (Option != opt_unknown) usage("only one operation permitted"); if (strcmp(User, RealUser) != 0) { - fprintf(stderr, - "cannot use -u with -n or -c\n"); + fprintf(stderr, "cannot use -u with -n or -c\n"); exit(ERROR_EXIT); } Option = opt_hostset; @@ -309,8 +326,7 @@ static void parse_args(int argc, char *argv[]) { if (Option != opt_unknown) usage("only one operation permitted"); if (strcmp(User, RealUser) != 0) { - fprintf(stderr, - "cannot use -u with -n or -c\n"); + fprintf(stderr, "cannot use -u with -n or -c\n"); exit(ERROR_EXIT); } Option = opt_hostget; @@ -333,22 +349,31 @@ static void parse_args(int argc, char *argv[]) { optind++; } - if (Option != opt_unknown) { - if (argv[optind] != NULL) - usage("no arguments permitted after this option"); + if (Option == opt_unknown) { + /* replace is the default option */ + Option = opt_replace; } - else { + + if (Option == opt_replace || Option == opt_test) { if (argv[optind] != NULL) { - Option = opt_replace; if (strlen(argv[optind]) >= sizeof Filename) usage("filename too long"); (void) strcpy(Filename, argv[optind]); + optind++; + } + else if (isatty(STDIN_FILENO)) { + usage("file name or - (for stdin) must be specified"); + } + else { + strcpy(Filename, "-"); } - else - usage("file name or - (for stdin) must be specified for replace"); } - if (Option == opt_replace) { + if (Option != opt_unknown && argv[optind] != NULL) { + usage("no arguments permitted after this option"); + } + + if (Filename[0] != '\0') { if (!strcmp(Filename, "-")) NewCrontab = stdin; else { @@ -375,8 +400,7 @@ static void parse_args(int argc, char *argv[]) { } if ((sb.st_mode & S_IFMT) == S_IFDIR) { fprintf(stderr, - "cannot replace crontab with a directory: %s\n", - Filename); + "invalid crontab file: '%s' is a directory\n", Filename); fclose(NewCrontab); exit(ERROR_EXIT); } @@ -395,8 +419,9 @@ static void list_cmd(void) { char n[MAX_FNAME]; FILE *f; int ch; - const int is_tty = isatty(STDOUT); + const int colorize = isatty(STDOUT) && getenv("NO_COLOR") == NULL; int new_line = 1; + int in_comment = 0; log_it(RealUser, Pid, "LIST", User, 0); if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/')) { @@ -413,19 +438,28 @@ static void list_cmd(void) { /* file is open. copy to stdout, close. */ - Set_LineNum(1) + Set_LineNum(1); while (EOF != (ch = get_char(f))) { - if (is_tty && new_line) { - if (ch == '#') { + if (colorize) { + if (!in_comment && new_line && ch == '#') { + in_comment = 1; fputs(COMMENT_COLOR, stdout); } - else { + if (in_comment && ch == '\n') { + in_comment = 0; fputs(RESET_COLOR, stdout); } } putchar(ch); new_line = ch == '\n'; } + /* no new line at EOF */ + if (colorize && !new_line) { + putchar('\n'); + fputs(ERROR_COLOR "No end-of-line character at the end of file" + RESET_COLOR, stdout); + putchar('\n'); + } fclose(f); } @@ -469,8 +503,7 @@ static const char *tmp_path(void) { return tmpdir ? tmpdir : "/tmp"; } -static char *host_specific_filename(const char *prefix, const char *suffix) -{ +static char *host_specific_filename(const char *prefix, const char *suffix) { /* * For cluster-wide use, where there is otherwise risk of the same * name being generated on more than one host at once, insert hostname @@ -551,14 +584,14 @@ static void edit_cmd(void) { goto fatal; } - Set_LineNum(1) - /* - * NHEADER_LINES processing removed for clarity - * (NHEADER_LINES == 0 in all Red Hat crontabs) - */ - /* copy the rest of the crontab (if any) to the temp file. - */ - if (EOF != ch) + Set_LineNum(1); + /* + * NHEADER_LINES processing removed for clarity + * (NHEADER_LINES == 0 in all Red Hat crontabs) + */ + /* copy the rest of the crontab (if any) to the temp file. + */ + if (EOF != ch) while (EOF != (ch = get_char(f))) putc(ch, NewCrontab); @@ -755,21 +788,31 @@ static void edit_cmd(void) { log_it(RealUser, Pid, "END EDIT", User, 0); } +/* +* Check if crontab file can be installed or not +*/ +static int test_cmd(void) { + if (check_syntax(NewCrontab) < 0) { + fprintf(stderr, "Invalid crontab file. Syntax issues were found.\n"); + return (-2); + } + else { + fprintf(stderr, "No syntax issues were found in the crontab file.\n"); + } + return (0); +} + /* returns 0 on success * -1 on syntax error * -2 on install error */ static int replace_cmd(void) { - char n[MAX_FNAME], envstr[MAX_ENVSTR]; + char n[MAX_FNAME]; FILE *tmp; - int ch, eof, fd; + int ch, fd; int error = 0; - entry *e; uid_t file_owner; - char **envp; char *safename; - int envs = 0, entries = 0; - safename = host_specific_filename("#tmp", "XXXXXXXXXX"); if (!safename || !glue_strings(TempFilename, sizeof TempFilename, SPOOL_DIR, @@ -808,8 +851,8 @@ static int replace_cmd(void) { /* copy the crontab to the tmp */ rewind(NewCrontab); - Set_LineNum(1) - while (EOF != (ch = get_char(NewCrontab))) + Set_LineNum(1); + while (EOF != (ch = get_char(NewCrontab))) putc(ch, tmp); if (ftruncate(fileno(tmp), ftell(tmp)) == -1) { fprintf(stderr, "%s: error while writing new crontab to %s\n", @@ -827,74 +870,14 @@ static int replace_cmd(void) { } rewind(tmp); - /* check the syntax of the file being installed. - */ - - /* BUG: was reporting errors after the EOF if there were any errors - * in the file proper -- kludged it by stopping after first error. - * vix 31mar87 - */ - Set_LineNum(1 - NHEADER_LINES) - CheckErrorCount = 0; - eof = FALSE; - - envp = env_init(); - if (envp == NULL) { - fprintf(stderr, "%s: Cannot allocate memory.\n", ProgramName); + if ((error = check_syntax(tmp)) < 0) { + fprintf(stderr, "Invalid crontab file, can't install.\n"); fclose(tmp); - error = -2; goto done; } - while (!CheckErrorCount && !eof) { - if (!skip_comments(tmp)) { - check_error("too many garbage characters"); - break; - } - switch (load_env(envstr, tmp)) { - case ERR: - /* check for data before the EOF */ - if (envstr[0] != '\0') { - Set_LineNum(LineNumber + 1); - check_error("premature EOF"); - } - eof = TRUE; - break; - case FALSE: - e = load_entry(tmp, check_error, pw, envp); - if (e) { - ++entries; - free_entry(e); - } - break; - case TRUE: - ++envs; - break; - } - } - env_free(envp); - if (envs > MAX_USER_ENVS) { - fprintf(stderr, "More than %d environment variables in crontab file, can't install.\n", MAX_USER_ENVS); - fclose(tmp); - error = -1; - goto done; - } - - if (entries > MAX_USER_ENTRIES) { - fprintf(stderr, "More than %d entries in crontab file, can't install.\n", MAX_USER_ENTRIES); - fclose(tmp); - error = -1; - goto done; - } - - if (CheckErrorCount != 0) { - fprintf(stderr, "errors in crontab file, can't install.\n"); - fclose(tmp); - error = -1; - goto done; - } - - file_owner = (getgid() == geteuid() && getgid() == getegid()) ? ROOT_UID : pw->pw_uid; + file_owner = (getgid() == geteuid() && + getgid() == getegid()) ? ROOT_UID : pw->pw_uid; #ifdef HAVE_FCHOWN if (fchown(fileno(tmp), file_owner, (gid_t)-1) < OK) { @@ -946,16 +929,90 @@ static int replace_cmd(void) { return (error); } +/* + * Check the syntax of a crontab file + * Returns: + * 0 no syntax issues + * -1 syntax issue (can be fixed by user) + * -2 any other error, which can not be fixed by user + */ +static int check_syntax(FILE * crontab_file) { + char **envp = env_init(); + int eof = FALSE; + int envs = 0, entries = 0; + + CheckErrorCount = 0; + Set_LineNum(1 - NHEADER_LINES); + + if (envp == NULL) { + fprintf(stderr, "%s: Cannot allocate memory.\n", ProgramName); + return (-2); + } + + while (!CheckErrorCount && !eof) { + char envstr[MAX_ENVSTR]; + entry *e; + + if (!skip_comments(crontab_file)) { + check_error + ("too much non-parseable content (comments, empty lines, spaces)"); + break; + } + + switch (load_env(envstr, crontab_file)) { + case ERR: + /* check for data before the EOF */ + if (envstr[0] != '\0') { + Set_LineNum(LineNumber + 1); + check_error("premature EOF"); + } + eof = TRUE; + break; + case FALSE: + e = load_entry(crontab_file, check_error, pw, envp); + if (e) { + ++entries; + free_entry(e); + } + break; + case TRUE: + ++envs; + break; + } + } + env_free(envp); + + if (envs > MAX_USER_ENVS) { + fprintf(stderr, + "There are too many environment variables in the crontab file. Limit: %d\n", + MAX_USER_ENVS); + return (-1); + } + + if (entries > MAX_USER_ENTRIES) { + fprintf(stderr, + "There are too many entries in the crontab file. Limit: %d\n", + MAX_USER_ENTRIES); + return (-1); + } + + if (CheckErrorCount != 0) { + return (-1); + } + + return 0; +} + static int hostset_cmd(void) { char n[MAX_FNAME]; FILE *tmp; int fd; int error = 0; char *safename; - + if (!HostSpecified) gethostname(Host, sizeof Host); - + safename = host_specific_filename("#tmp", "XXXXXXXXXX"); if (!safename || !glue_strings(TempFilename, sizeof TempFilename, SPOOL_DIR, safename, '/')) { @@ -977,7 +1034,7 @@ static int hostset_cmd(void) { (void) signal(SIGINT, die); (void) signal(SIGQUIT, die); - (void) fchmod(fd, 0600); /* not all mkstemp() implementations do this */ + (void) fchmod(fd, 0600); /* not all mkstemp() implementations do this */ if (fprintf(tmp, "%s\n", Host) < 0 || fclose(tmp) == EOF) { fprintf(stderr, "%s: error while writing to %s\n", diff --git a/src/database.c b/src/database.c index 1986620..bff0256 100644 --- a/src/database.c +++ b/src/database.c @@ -469,10 +469,6 @@ int load_database(cron_db * old_db) { statbuf.st_mtime = 0; } else { - /* As pointed out in Red Hat bugzilla 198019, with modern Linux it - * is possible to modify a file without modifying the mtime of the - * containing directory. Hence, we must check the mtime of each file: - */ max_mtime(SPOOL_DIR, &statbuf); } @@ -494,16 +490,16 @@ int load_database(cron_db * old_db) { /* 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(), and - * so is guaranteed to be different than the stat() mtime the first - * time this function is called. + * 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 == TMIN(now - 1, TMAX(crond_stat.st_mtime, - TMAX(statbuf.st_mtime, syscron_stat.st_mtime))) + 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)); @@ -563,7 +559,8 @@ int load_database(cron_db * old_db) { if (not_a_crontab(dp)) continue; - strncpy(fname, dp->d_name, NAME_MAX + 1); + strncpy(fname, dp->d_name, NAME_MAX); + fname[NAME_MAX] = '\0'; if (!glue_strings(tabname, sizeof tabname, SPOOL_DIR, fname, '/')) continue; /* XXX log? */ diff --git a/src/do_command.c b/src/do_command.c index ed56775..87f996f 100644 --- a/src/do_command.c +++ b/src/do_command.c @@ -35,6 +35,7 @@ #include "funcs.h" #include "globals.h" #include "structs.h" +#include "cronie_common.h" #ifndef isascii # define isascii(c) ((unsigned)(c)<=0177) @@ -89,6 +90,8 @@ void do_command(entry * e, user * u) { 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; @@ -126,6 +129,24 @@ static int child_process(entry * e, char **jobenv) { 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 */ @@ -194,6 +215,9 @@ static int child_process(entry * e, char **jobenv) { 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); } @@ -473,7 +497,10 @@ static int child_process(entry * e, char **jobenv) { *nl = ' '; fprintf(mail, "Content-Type: %s\n", content_type); } - if (content_transfer_encoding != NULL) { + 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') @@ -589,6 +616,12 @@ static int child_process(entry * e, char **jobenv) { 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; } diff --git a/src/entry.c b/src/entry.c index e3a3b1d..bb7cb62 100644 --- a/src/entry.c +++ b/src/entry.c @@ -62,9 +62,22 @@ static const char *ecodes[] = { "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 *[], int, FILE *), -get_number(int *, int, const char *[], int, FILE *, const char *), +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) { @@ -179,7 +192,7 @@ entry *load_entry(FILE * file, void (*error_func) (), struct passwd *pw, 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 |= DOW_STAR; + e->flags |= DOM_STAR; } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { bit_set(e->minute, 0); @@ -301,6 +314,17 @@ entry *load_entry(FILE * file, void (*error_func) (), struct passwd *pw, 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) { @@ -456,11 +480,14 @@ get_list(bitstr_t * bits, int low, int high, const char *names[], /* 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, ch, file))) + if (EOF == (ch = get_range(bits, low, high, names, file))) return (EOF); if (ch == ',') - ch = get_char(file); + continue; else done = TRUE; } @@ -475,144 +502,197 @@ get_list(bitstr_t * bits, int low, int high, const char *names[], 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[], - int ch, FILE * file) { + FILE * file) { /* range = number | number "-" number [ "/" number ] + * | [number] "~" [number] */ + + int ch, i, num1, num2, num3; - int i, num1, num2, num3; + /* default value for step + */ + num3 = 1; + range_state_t state = R_START; - Debug(DPARS | DEXT, ("get_range()...entering, exit won't show\n")); - - if (ch == '*') { - /* '*' means "first-last" but can still be modified by /step - */ - num1 = low; - num2 = high; - ch = get_char(file); - if (ch == EOF) - return (EOF); - } - else { - ch = get_number(&num1, low, names, ch, file, ",- \t\n"); - if (ch == EOF) - return (EOF); - - if (ch != '-') { - /* not a range, it's a single number. - */ - if (EOF == set_element(bits, low, high, num1)) { + 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); - return (EOF); - } - return (ch); - } - else { - /* eat the dash - */ - ch = get_char(file); - if (ch == EOF) + if (get_number(&num1, low, names, file) != EOF) { + state = R_NUM1; + break; + } return (EOF); - /* get the number following the dash - */ - ch = get_number(&num2, low, names, ch, file, "/, \t\n"); - if (ch == EOF || num1 > num2) + 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 = random() % (num2 - num1 + 1) + num1; + break; + + + default: + /* We should never get here + */ return (EOF); } } - - /* check for step size - */ - if (ch == '/') { - /* eat the slash - */ - ch = get_char(file); - if (ch == EOF) - return (EOF); - - /* get the step size -- note: we don't pass the - * names here, because the number is not an - * element id, it's a step size. 'low' is - * sent as a 0 since there is no offset either. - */ - ch = get_number(&num3, 0, PPC_NULL, ch, file, ", \t\n"); - if (ch == EOF || num3 == 0) - return (EOF); - } - else { - /* no step. default==1. - */ - num3 = 1; - } - - /* num1 (through i) will be validated by set_element() below, but num2 - * and num3 are merely used as loop condition and increment, and must - * be validated separately. - */ - if (num2 < low || num2 > high || num3 > high) + if (state != R_FINISH || ch == EOF) return (EOF); - /* range. set all elements from num1 to num2, stepping - * by num3. (the step is a downward-compatible extension - * proposed conceptually by bob@acornrc, syntactically - * designed then implemented by paul vixie). - */ for (i = num1; i <= num2; i += num3) if (EOF == set_element(bits, low, high, i)) { unget_char(ch, file); return (EOF); } - - return (ch); + return ch; } static int -get_number(int *numptr, int low, const char *names[], int ch, FILE * file, - const char *terms) { +get_number(int *numptr, int low, const char *names[], FILE * file) { char temp[MAX_TEMPSTR], *pc; - int len, i; + int len, i, ch; + char *endptr; pc = temp; len = 0; - /* first look for a number */ - while (isdigit((unsigned char) ch)) { + /* get all alnum characters available */ + while (isalnum((ch = get_char(file)))) { if (++len >= MAX_TEMPSTR) goto bad; *pc++ = (char)ch; - ch = get_char(file); } *pc = '\0'; - if (len != 0) { - /* got a number, check for valid terminator */ - if (!strchr(terms, ch)) - goto bad; - *numptr = atoi(temp); - return (ch); + 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) { - while (isalpha((unsigned char) ch)) { - if (++len >= MAX_TEMPSTR) - goto bad; - *pc++ = (char)ch; - ch = get_char(file); - } - *pc = '\0'; - if (len != 0 && strchr(terms, ch)) { - for (i = 0; names[i] != NULL; i++) { - Debug(DPARS | DEXT, - ("get_num, compare(%s,%s)\n", names[i], temp)); - if (!strcasecmp(names[i], temp)) { - *numptr = i + low; - return (ch); - } + 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: @@ -623,7 +703,7 @@ get_number(int *numptr, int low, const char *names[], int ch, FILE * file, 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) + if (number < low || number > high) return (EOF); bit_set(bits, (number - low)); diff --git a/src/env.c b/src/env.c index 3336c51..c737237 100644 --- a/src/env.c +++ b/src/env.c @@ -191,7 +191,7 @@ int load_env(char *envstr, FILE * f) { Debug(DPARS, ("load_env, read <%s>\n", envstr)); - str = envstr; + val = str = envstr; state = NAMEI; quotechar = '\0'; c = envstr; diff --git a/src/job.c b/src/job.c index 0d81240..3af7972 100644 --- a/src/job.c +++ b/src/job.c @@ -72,8 +72,8 @@ void job_add(entry * e, user * u) { e->envp = tenvp; } else { log_it(uname, getpid(), "ERROR", "getpwnam() failed - user unknown",errno); - Debug(DSCH | DEXT, ("%s:%d pid=%d time=%ld getpwnam(%s) failed errno=%d error=%s\n", - __FILE__,__LINE__,getpid(),time(NULL),uname,errno,strerror(errno))); + Debug(DSCH | DEXT, ("%s:%d pid=%d time=%lld getpwnam(%s) failed errno=%d error=%s\n", + __FILE__,__LINE__,getpid(),(long long)time(NULL),uname,errno,strerror(errno))); return; } diff --git a/src/macros.h b/src/macros.h index cba5fb2..d50e981 100644 --- a/src/macros.h +++ b/src/macros.h @@ -59,7 +59,7 @@ #define ROOT_UID 0 /* don't change this, it really must be root */ #define ROOT_USER "root" /* ditto */ #define MAX_USER_ENVS 1000 /* maximum environment variables in user's crontab */ -#define MAX_USER_ENTRIES 1000 /* maximum crontab entries in user's crontab */ +#define MAX_USER_ENTRIES 10000 /* maximum crontab entries in user's crontab */ #define MAX_GARBAGE 32768 /* max num of chars of comments and whitespaces between entries */ #define MAX_CLOSE_FD 10000 /* max fd num to close when spawning a child process */ diff --git a/src/pathnames.h b/src/pathnames.h index 1d716be..3d1e7e0 100644 --- a/src/pathnames.h +++ b/src/pathnames.h @@ -36,10 +36,14 @@ * PIDDIR must end in '/'. * (Don't ask why the default is "/etc/".) */ -#ifdef _PATH_VARRUN -# define PIDDIR _PATH_VARRUN +#ifdef CRON_PID_DIR +# define PIDDIR CRON_PID_DIR "/" #else -# define PIDDIR SYSCONFDIR "/" +# ifdef _PATH_VARRUN +# define PIDDIR _PATH_VARRUN +# else +# define PIDDIR SYSCONFDIR "/" +# endif #endif #define PIDFILE "crond.pid" #define _PATH_CRON_PID PIDDIR PIDFILE diff --git a/src/pw_dup.c b/src/pw_dup.c index ea787cd..c6f7b00 100644 --- a/src/pw_dup.c +++ b/src/pw_dup.c @@ -121,6 +121,7 @@ pw_dup(const struct passwd *pw) { cp += ssize; } + /* cppcheck-suppress[memleak symbolName=cp] memory originally pointed to by cp returned via newpw */ return (newpw); } diff --git a/src/security.c b/src/security.c index d4d768e..d61fb2a 100644 --- a/src/security.c +++ b/src/security.c @@ -121,7 +121,7 @@ int cron_set_job_security_context(entry *e, user *u ATTRIBUTE_UNUSED, * Ensure that these jobs never run in the same minute: */ minutely_time = time(NULL); - Debug(DSCH, ("Minute-ly job. Recording time %lu\n", minutely_time)); + Debug(DSCH, ("Minute-ly job. Recording time %lld\n", (long long)minutely_time)); } #ifdef WITH_PAM