Compare commits
No commits in common. "trunk" and "keyring" have entirely different histories.
52 changed files with 0 additions and 9327 deletions
|
|
@ -1,10 +0,0 @@
|
|||
;; Per-directory local variables for GNU Emacs 23 and later.
|
||||
|
||||
((nil . ((fill-column . 78)
|
||||
(tab-width . 8)))
|
||||
(c-mode . ((c-file-style . "gnu")
|
||||
(indent-tabs-mode . nil)))
|
||||
(scheme-mode
|
||||
.
|
||||
((indent-tabs-mode . nil)
|
||||
(eval . (put 'mcron-error 'scheme-indent-function 1)))))
|
||||
47
.gitignore
vendored
47
.gitignore
vendored
|
|
@ -1,47 +0,0 @@
|
|||
*.[oa]
|
||||
*.go
|
||||
*.log
|
||||
*.trs
|
||||
*~
|
||||
.deps
|
||||
.dirstamp
|
||||
/bin/cron
|
||||
/bin/crontab
|
||||
/bin/mcron
|
||||
/build-aux/ar-lib
|
||||
/build-aux/compile
|
||||
/build-aux/config.guess
|
||||
/build-aux/config.sub
|
||||
/build-aux/depcomp
|
||||
/build-aux/install-sh
|
||||
/build-aux/mdate-sh
|
||||
/build-aux/missing
|
||||
/build-aux/test-driver
|
||||
/build-aux/texinfo.tex
|
||||
/doc/config.texi
|
||||
/doc/cron.8
|
||||
/doc/crontab.1
|
||||
/doc/mcron.1
|
||||
/doc/mcron.info
|
||||
/doc/stamp-vti
|
||||
/doc/version.texi
|
||||
/mdate-sh
|
||||
INSTALL
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
autom4te.cache
|
||||
compile
|
||||
config.cache
|
||||
config.h
|
||||
config.h.in
|
||||
config.log
|
||||
config.scm
|
||||
config.status
|
||||
configure
|
||||
depcomp
|
||||
install-sh
|
||||
missing
|
||||
pre-inst-env
|
||||
stamp-h1
|
||||
texinfo.tex
|
||||
|
|
@ -1 +0,0 @@
|
|||
1.1.1
|
||||
6
AUTHORS
6
AUTHORS
|
|
@ -1,6 +0,0 @@
|
|||
Dale Mellor <mcron-lsfnyl@rdmp.org>
|
||||
Mathieu Lirzin <mthl@gnu.org>
|
||||
Sergey Poznyakoff <cray@gnu.org.ua>
|
||||
Ludovic Courtès <ludo@gnu.org>
|
||||
宋文武 <iyzsong@member.fsf.org>
|
||||
Efraim Flashner <efraim@flashner.co.il>
|
||||
674
COPYING
674
COPYING
|
|
@ -1,674 +0,0 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If 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 convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
Normally a ChangeLog is generated at "make dist" time and available in
|
||||
source tarballs.
|
||||
|
||||
If not, see the Git commit log at <http://git.sv.gnu.org/cgit/mcron.git/>.
|
||||
147
ChangeLog.old
147
ChangeLog.old
|
|
@ -1,147 +0,0 @@
|
|||
2014-05-25 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* Juggled build infrastructure so that we can make the minimal man
|
||||
page in the proper autotools way.
|
||||
|
||||
* configure.ac: version to 1.0.8.
|
||||
|
||||
2014-04-28 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* We now run against, and require, guile-2.0.
|
||||
|
||||
* configure.ac: version to 1.0.7.
|
||||
|
||||
2012-02-04 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* main.scm: added search for initial files in
|
||||
$XDG_CONFIG_HOME/cron directory, defaulting to ~/.config/cron if
|
||||
the environment variable is not set) as well as in ~/.cron
|
||||
directory (this is in line with the current FreeDesktop.org
|
||||
standards).
|
||||
|
||||
2010-06-13 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* configure.ac: added --enable-no-vixie-clobber argument to
|
||||
configure so that the root user can avoid overwriting a legacy
|
||||
cron installation.
|
||||
|
||||
* mcron.1: added simple, minimal man page using help2man (the
|
||||
texinfo file is still the primary documentation source).
|
||||
|
||||
* makefile.am: replaced use of mkinstalldirs with install; the
|
||||
former is not supplied with the latest automake (1.11).
|
||||
|
||||
2008-02-21 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* ALL FILES: Replaced version 2 GPL notices with version 3 ones.
|
||||
|
||||
* makefile.am: Do not remove COPYING file with make
|
||||
maintainer-clean; if you do it will eventually get replaced with
|
||||
the old version 2 GPL by a subsequent automake.
|
||||
|
||||
* configure.ac: Bumped version to 1.0.4.
|
||||
|
||||
2008-01-25 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* main.scm (command-type): Files which are listed on the command
|
||||
line are assumed to be guile configurations if they do not end in
|
||||
.guile or .vixie (previously they were silently ignored).
|
||||
|
||||
* main.scm: Argument to --schedule is no longer optional (the
|
||||
options system goes really screwy with optional values, usually
|
||||
pulling the first non-option argument as a value if one was not
|
||||
intended!)
|
||||
|
||||
* makefile.am: Moved target-specific CFLAGS and LDFLAGS to global
|
||||
AM_* variables, to remove problem with automake requiring
|
||||
AM_PROGS_CC_C_O in configure.ac (!)
|
||||
|
||||
* Version is currently at 1.0.3.
|
||||
|
||||
2005-09-02 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* makefile.am, mcron.c.template (main): Modified install-exec-hook
|
||||
so that a proper installation of a Vixie-compatible cron only
|
||||
takes place if we are root - otherwise only mcron is installed as
|
||||
a user-owned program. The guile modules are now installed under
|
||||
mcron's shared data directory, not guile's global directories.
|
||||
|
||||
* mcron-core.scm: Removed job:advance-time, put the code inline
|
||||
where it was called, and changed the instance in the main loop to
|
||||
compute the new time based on the current-time, rather than the
|
||||
previous job time (this makes things behave more reasonably when a
|
||||
laptop awakes from suspend mode).
|
||||
|
||||
* Bumped version to 1.0.2.
|
||||
|
||||
2004-05-15 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* Modified all auxiliary files to reflect that the package is now
|
||||
properly homed at www.gnu.org.
|
||||
|
||||
* Bumped version to 1.0.1.
|
||||
|
||||
2003-12-11 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* Modified all auxiliary files to reflect that we are now a GNU
|
||||
package.
|
||||
|
||||
* Bumped version to 1.0.0.
|
||||
|
||||
2003-12-07 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* configure.ac: Added switches for files and directories used by
|
||||
mcron: --spool-dir, --socket-file, --allow-file, --deny-file,
|
||||
--pid-file and --tmp-dir. All the code has been modified to use
|
||||
these configure options (including the source for the texinfo
|
||||
manual).
|
||||
|
||||
2003-12-05 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* configure.ac: Added test for guile version >= 1.6.4.
|
||||
|
||||
* bumped version to 0.99.4.
|
||||
|
||||
2003-08-03 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* Third cut, fully functional, modular, production quality, still
|
||||
needs testing...
|
||||
|
||||
* Pulled all functionality into modules, so it can be incorporated
|
||||
into other programs.
|
||||
|
||||
* Bumped version to 0.99.3.
|
||||
|
||||
2003-07-20 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* Second cut, now _really_ fully functional (100% Vixie
|
||||
compatible), production quality code, still needs lots of testing
|
||||
doing...
|
||||
|
||||
* Converted from SIGUP-/var/cron/update to select-/var/cron/socket
|
||||
method of communication between crontab and cron.
|
||||
|
||||
* Added implicit job which checks every minute for updates to
|
||||
/etc/crontab.
|
||||
|
||||
* Removed --enable-vixie configuration option - the Vixie programs
|
||||
are built and installed by default now.
|
||||
|
||||
* Bumped version to 0.99.2.
|
||||
|
||||
2003-06-28 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
|
||||
* First cut, fully functional, production quality code, just needs
|
||||
testing...
|
||||
|
||||
* Broken/incomplete Guile prevents vixie compatibility from
|
||||
working - this has been disabled by default in the configuration.
|
||||
|
||||
* Version set at 0.99.1
|
||||
|
||||
________________________________________________________________________________
|
||||
Copyright (C) 2003, 2005, 2006, 2014, 2015 Dale Mellor
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved.
|
||||
90
HACKING
90
HACKING
|
|
@ -1,90 +0,0 @@
|
|||
These notes intend to help people working on the checked-out sources.
|
||||
These requirements do not apply when building from a distribution tarball.
|
||||
|
||||
* First Git checkout
|
||||
|
||||
You can get a copy of the source repository like this:
|
||||
|
||||
$ git clone git://git.sv.gnu.org/mcron
|
||||
$ cd mcron
|
||||
|
||||
The next step is to get and check other files needed to build, which are
|
||||
extracted from other source packages:
|
||||
|
||||
$ ./bootstrap
|
||||
|
||||
And there you are! Just
|
||||
|
||||
$ ./configure
|
||||
$ make
|
||||
|
||||
At this point, there should be no difference between your local copy, and the
|
||||
Git master copy:
|
||||
|
||||
$ git diff
|
||||
|
||||
should output no difference.
|
||||
|
||||
Enjoy!
|
||||
|
||||
* Submitting patches
|
||||
|
||||
If you develop a fix or a new feature, please send it to the appropriate
|
||||
bug-reporting address as reported by the --help option of each program. One
|
||||
way to do this is to use vc-dwim <http://www.gnu.org/software/vc-dwim/>), as
|
||||
follows.
|
||||
|
||||
Run the command "vc-dwim --help", copy its definition of the
|
||||
"git-changelog-symlink-init" function into your shell, and then run this
|
||||
function at the top-level directory of the package.
|
||||
|
||||
Edit the (empty) ChangeLog file that this command creates, creating a
|
||||
properly-formatted entry according to the GNU coding standards
|
||||
<http://www.gnu.org/prep/standards/html_node/Change-Logs.html>.
|
||||
|
||||
Make your changes.
|
||||
|
||||
Run the command "vc-dwim" and make sure its output (the diff of all your
|
||||
changes) looks good.
|
||||
|
||||
Run "vc-dwim --commit".
|
||||
|
||||
Run the command "git format-patch --stdout -1", and email its output in,
|
||||
using the output's subject line.
|
||||
|
||||
* Updating auxilary scripts
|
||||
|
||||
Fetch new versions of the files that are maintained in other GNU
|
||||
repositories by running "make fetch". In case any file in the
|
||||
Mcron repository has been updated, commit and re-run the testsuite.
|
||||
|
||||
* Code coverage
|
||||
|
||||
Assuming 'lcov' is installed, it is possible to check the actual code
|
||||
coverage achieved by the test suite by running the following commands:
|
||||
|
||||
$ make check SCM_LOG_DRIVER_FLAGS="--coverage=yes"
|
||||
$ genhtml tests/*.info --output-directory out
|
||||
|
||||
-----
|
||||
|
||||
Copyright © 2002-2017 Free Software Foundation, Inc.
|
||||
Copyright © 2017, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Local Variables:
|
||||
mode: outline
|
||||
fill-column: 78
|
||||
End:
|
||||
254
Makefile.am
254
Makefile.am
|
|
@ -1,254 +0,0 @@
|
|||
## Process this file with automake to produce Makefile.in.
|
||||
# Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
# Copyright © 2015, 2016, 2017, 2018, 2020 Mathieu Lirzin <mthl@gnu.org>
|
||||
#
|
||||
# This file is part of GNU Mcron.
|
||||
#
|
||||
# GNU Mcron is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# GNU Mcron is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
## ---------- ##
|
||||
## Programs. ##
|
||||
## ---------- ##
|
||||
|
||||
bin_SCRIPTS = bin/mcron
|
||||
noinst_SCRIPTS =
|
||||
|
||||
if MULTI_USER
|
||||
bin_SCRIPTS += bin/crontab
|
||||
sbin_SCRIPTS = bin/cron
|
||||
else
|
||||
noinst_SCRIPTS += bin/cron bin/crontab
|
||||
endif
|
||||
|
||||
# wrapper to be used in the build environment and for running tests.
|
||||
noinst_SCRIPTS += pre-inst-env
|
||||
|
||||
## --------------- ##
|
||||
## Guile modules. ##
|
||||
## --------------- ##
|
||||
|
||||
# Root directory used for installing Guile modules.
|
||||
guilesitedir = $(datarootdir)/guile/site/$(GUILE_EFFECTIVE_VERSION)
|
||||
# Root directory used for installing Guile compiled modules.
|
||||
guilesitegodir = $(libdir)/guile/$(GUILE_EFFECTIVE_VERSION)/site-ccache
|
||||
|
||||
pkgmoduledir = $(guilesitedir)/$(PACKAGE)
|
||||
pkgmodule_DATA = src/mcron/config.scm
|
||||
dist_pkgmodule_DATA = \
|
||||
src/mcron/base.scm \
|
||||
src/mcron/environment.scm \
|
||||
src/mcron/job-specifier.scm \
|
||||
src/mcron/redirect.scm \
|
||||
src/mcron/utils.scm \
|
||||
src/mcron/vixie-specification.scm \
|
||||
src/mcron/vixie-time.scm
|
||||
|
||||
# Alias for 'src/mcron/base.scm' kept for backward compatibility.
|
||||
dist_pkgmodule_DATA += src/mcron/core.scm
|
||||
|
||||
pkgmodulegodir = $(guilesitegodir)/$(PACKAGE)
|
||||
pkgmodulego_DATA = \
|
||||
$(dist_pkgmodule_DATA:.scm=.go) \
|
||||
src/mcron/config.go
|
||||
|
||||
pkgscriptdir = $(pkgmoduledir)/scripts
|
||||
dist_pkgscript_DATA = \
|
||||
src/mcron/scripts/cron.scm \
|
||||
src/mcron/scripts/crontab.scm \
|
||||
src/mcron/scripts/mcron.scm
|
||||
|
||||
pkgscriptgodir = $(pkgmodulegodir)/scripts
|
||||
pkgscriptgo_DATA = $(dist_pkgscript_DATA:.scm=.go)
|
||||
|
||||
compiled_modules = \
|
||||
$(pkgmodulego_DATA) \
|
||||
$(pkgscriptgo_DATA)
|
||||
|
||||
CLEANFILES = $(compiled_modules) bin/crontab bin/cron bin/mcron
|
||||
DISTCLEANFILES = src/mcron/config.scm
|
||||
|
||||
# Unset 'GUILE_LOAD_COMPILED_PATH' altogether while compiling. Otherwise, if
|
||||
# $GUILE_LOAD_COMPILED_PATH contains $(pkgmoduledir), we may find .go files
|
||||
# in there that are newer than the local .scm files (for instance because the
|
||||
# user ran 'make install' recently). When that happens, we end up loading
|
||||
# those previously-installed .go files, which may be stale, thereby breaking
|
||||
# the whole thing. Set GUILE_AUTO_COMPILE to 0 to avoid auto-compiling guild
|
||||
# as a consequence of the previous hack.
|
||||
#
|
||||
# XXX: Use the C locale for when Guile lacks
|
||||
# <http://git.sv.gnu.org/cgit/guile.git/commit/?h=stable-2.0&id=e2c6bf3866d1186c60bacfbd4fe5037087ee5e3f>.
|
||||
.scm.go:
|
||||
$(guilec_verbose)$(MKDIR_P) `dirname "$@"`; \
|
||||
export GUILE_AUTO_COMPILE=0; unset GUILE_LOAD_COMPILED_PATH; \
|
||||
LC_ALL=C \
|
||||
$(top_builddir)/pre-inst-env $(GUILD) compile \
|
||||
--load-path="$(builddir)/src" \
|
||||
--load-path="$(srcdir)/src" \
|
||||
--warn=format --warn=unbound-variable --warn=arity-mismatch \
|
||||
--target="$(host)" --output="$@" "$<" $(devnull_verbose)
|
||||
|
||||
|
||||
bin/% : src/%.in Makefile
|
||||
$(AM_V_GEN)$(MKDIR_P) bin ; \
|
||||
sed -e 's,%PREFIX%,${prefix},g' \
|
||||
-e 's,%modsrcdir%,${guilesitedir},g' \
|
||||
-e 's,%modbuilddir%,${guilesitegodir},g' \
|
||||
-e 's,%localstatedir%,${localstatedir},g' \
|
||||
-e 's,%pkglibdir%,${pkglibdir},g' \
|
||||
-e 's,%sysconfdir%,${sysconfdir},g' \
|
||||
-e 's,%localedir%,${localedir},g' \
|
||||
-e 's,%VERSION%,@VERSION@,g' \
|
||||
-e 's,%PACKAGE_BUGREPORT%,@PACKAGE_BUGREPORT@,g' \
|
||||
-e 's,%PACKAGE_NAME%,@PACKAGE_NAME@,g' \
|
||||
-e 's,%PACKAGE_URL%,@PACKAGE_URL@,g' \
|
||||
-e 's,%GUILE%,$(GUILE),g' \
|
||||
$< > $@ ; \
|
||||
chmod a+x $@
|
||||
|
||||
|
||||
## ------------ ##
|
||||
## Test suite. ##
|
||||
## ------------ ##
|
||||
|
||||
TEST_EXTENSIONS = .scm .sh
|
||||
AM_TESTS_ENVIRONMENT = env GUILE_AUTO_COMPILE='0'
|
||||
|
||||
SH_LOG_COMPILER = ./pre-inst-env $(SHELL)
|
||||
|
||||
SCM_LOG_DRIVER = \
|
||||
$(builddir)/pre-inst-env $(GUILE) \
|
||||
$(srcdir)/build-aux/test-driver.scm
|
||||
|
||||
TESTS = \
|
||||
tests/basic.sh \
|
||||
tests/schedule.sh \
|
||||
tests/schedule-2.sh \
|
||||
tests/base.scm \
|
||||
tests/environment.scm \
|
||||
tests/job-specifier.scm \
|
||||
tests/redirect.scm \
|
||||
tests/utils.scm \
|
||||
tests/vixie-specification.scm \
|
||||
tests/vixie-time.scm
|
||||
|
||||
## -------------- ##
|
||||
## Distribution. ##
|
||||
## -------------- ##
|
||||
|
||||
EXTRA_DIST = \
|
||||
bootstrap \
|
||||
build-aux/guix.scm \
|
||||
HACKING \
|
||||
src/cron.in \
|
||||
src/crontab.in \
|
||||
src/mcron.in \
|
||||
tests/init.sh \
|
||||
$(TESTS)
|
||||
|
||||
## -------------- ##
|
||||
## Installation. ##
|
||||
## -------------- ##
|
||||
|
||||
# Sed command for Transforming program names.
|
||||
transform_exe = s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/
|
||||
|
||||
install-exec-hook:
|
||||
if MULTI_USER
|
||||
tcrontab=`echo crontab | sed '$(transform_exe)'`; \
|
||||
chmod u+s $(DESTDIR)$(bindir)/$${tcrontab}
|
||||
tcron=`echo cron | sed '$(transform_exe)'`; \
|
||||
chmod u+s $(DESTDIR)$(sbindir)/$${tcron}
|
||||
endif
|
||||
tmcron=`echo mcron | sed '$(transform_exe)'`;
|
||||
|
||||
installcheck-local:
|
||||
## Check that only expected programs are installed and configured
|
||||
tmcron=`echo mcron | sed '$(transform_exe)'`; \
|
||||
test -e $(DESTDIR)$(bindir)/$${tmcron}
|
||||
if MULTI_USER
|
||||
tcrontab=`echo crontab | sed '$(transform_exe)'`; \
|
||||
test -u $(DESTDIR)$(bindir)/$${tcrontab}
|
||||
tcron=`echo cron | sed '$(transform_exe)'`; \
|
||||
test -e $(DESTDIR)$(sbindir)/$${tcron}
|
||||
else !MULTI_USER
|
||||
tcrontab=`echo crontab | sed '$(transform_exe)'`; \
|
||||
test ! -u $(DESTDIR)$(bindir)/$${tcrontab}
|
||||
tcron=`echo cron | sed '$(transform_exe)'`; \
|
||||
test ! -f $(DESTDIR)$(sbindir)/$${tcron}
|
||||
endif !MULTI_USER
|
||||
|
||||
## --------------- ##
|
||||
## Documentation. ##
|
||||
## --------------- ##
|
||||
|
||||
info_TEXINFOS = doc/mcron.texi
|
||||
doc_mcron_TEXINFOS = doc/fdl.texi
|
||||
nodist_doc_mcron_TEXINFOS = doc/config.texi
|
||||
dist_man_MANS = $(srcdir)/doc/mcron.1
|
||||
extra_mans = \
|
||||
$(srcdir)/doc/crontab.1 \
|
||||
$(srcdir)/doc/cron.8
|
||||
|
||||
if MULTI_USER
|
||||
dist_man_MANS += $(extra_mans)
|
||||
else
|
||||
# Build, distribute, but do not install the extra man pages.
|
||||
all-local: $(extra_mans)
|
||||
EXTRA_DIST += $(extra_mans)
|
||||
endif
|
||||
|
||||
# XXX: Allow the inclusion of 'doc/fdl.texi' and 'doc/config.texi' inside
|
||||
# 'doc/mcron.texi' for 'dvi' and 'pdf' targets.
|
||||
TEXI2DVI = texi2dvi -I doc
|
||||
|
||||
# The 'case' ensures the man pages are only generated if the corresponding
|
||||
# source script (the first prerequisite) has been changed. The second
|
||||
# prerequisites is solely meant to force these docs to be made only after
|
||||
# executables have been compiled.
|
||||
gen_man = \
|
||||
case '$?' in \
|
||||
*$<*) $(AM_V_P) && set -x || echo " HELP2MAN $@"; \
|
||||
LANGUAGE= $(top_builddir)/pre-inst-env $(HELP2MAN) \
|
||||
-s $$man_section -S GNU -p $(PACKAGE_TARNAME) -o $@ $$prog;; \
|
||||
*) : ;; \
|
||||
esac
|
||||
|
||||
$(srcdir)/doc/mcron.1: src/mcron/scripts/mcron.scm bin/mcron
|
||||
-@prog="bin/mcron"; man_section=1; $(gen_man)
|
||||
|
||||
$(srcdir)/doc/crontab.1: src/mcron/scripts/crontab.scm bin/crontab
|
||||
-@prog="bin/crontab"; man_section=1; $(gen_man)
|
||||
|
||||
$(srcdir)/doc/cron.8: src/mcron/scripts/cron.scm bin/cron
|
||||
-@prog="cron"; man_section=8; $(gen_man)
|
||||
|
||||
MAINTAINERCLEANFILES = $(dist_man_MANS) $(extra_mans)
|
||||
|
||||
## -------------- ##
|
||||
## Silent rules. ##
|
||||
## -------------- ##
|
||||
|
||||
guilec_verbose = $(guilec_verbose_@AM_V@)
|
||||
guilec_verbose_ = $(guilec_verbose_@AM_DEFAULT_V@)
|
||||
guilec_verbose_0 = @echo " GUILEC " $@;
|
||||
|
||||
devnull_verbose = $(devnull_verbose_@AM_V@)
|
||||
devnull_verbose_ = $(devnull_verbose_@AM_DEFAULT_V@)
|
||||
devnull_verbose_0 = >/dev/null
|
||||
|
||||
## ------------- ##
|
||||
## Maintenance. ##
|
||||
## ------------- ##
|
||||
|
||||
@MAINT_MAKEFILE@
|
||||
196
NEWS
196
NEWS
|
|
@ -1,196 +0,0 @@
|
|||
GNU Mcron NEWS -*- outline -*-
|
||||
|
||||
* Noteworthy changes in release 1.2.0 (2020-04-22) [stable]
|
||||
|
||||
** Improvements
|
||||
C code removed, mcron becomes 100% Guile.
|
||||
Make doc/mcron.texi gender neutral.
|
||||
Have src/mcron/scripts/mcron.scm (process-user-file): use read and eval
|
||||
instead of load.
|
||||
New tests added for extra checks.
|
||||
|
||||
* Noteworthy changes in release 1.1.4 (2020-04-12) [stable]
|
||||
|
||||
** Improvements
|
||||
Added missing #include directives
|
||||
Support Guile 3.0
|
||||
Call 'child-cleanup' when 'select' returns an empty set
|
||||
Avoid 'call-with-current-continuation'
|
||||
Date changes for Copyrights changed for 2020
|
||||
Email updates in documentation
|
||||
|
||||
* Noteworthy changes in release 1.1.3 (2019-11-17) [stable]
|
||||
|
||||
** Improvements
|
||||
Package contains configure script by default
|
||||
Authors file change (addition)
|
||||
Doc fix for 'every second sunday'
|
||||
guix.scm update
|
||||
|
||||
* Noteworthy changes in release 1.1.2 (2018-11-26) [stable]
|
||||
|
||||
** Improvements
|
||||
|
||||
The "--with-sendmail" configure variable has been added to allow the usage
|
||||
of a different Mail Transfert Agent (MTA) than 'sendmail -t'. The MTA must
|
||||
be able to guess the actual recipients from the 'To:' message header.
|
||||
|
||||
* Noteworthy changes in release 1.1.1 (2018-04-08) [stable]
|
||||
|
||||
** Bug fixes
|
||||
|
||||
The "--disable-multi-user" configure variable is not reversed anymore.
|
||||
'cron' and 'crontab' are now installed unless this option is used.
|
||||
|
||||
The programs now sets the GUILE_LOAD_PATH and GUILE_LOAD_COMPILED_PATH
|
||||
environment variables with the location of the installed Guile modules.
|
||||
|
||||
'next-year-from', 'next-year', 'next-month-from', 'next-month',
|
||||
'next-day-from', 'next-day', 'next-hour-from', 'next-hour',
|
||||
'next-minute-from', 'next-minute', 'next-second-from', and 'next-second' no
|
||||
longer crashes when passing an optional argument.
|
||||
[bug introduced in mcron-1.1]
|
||||
|
||||
** Improvements
|
||||
|
||||
Some basic tests for the installed programs can be run after 'make install'
|
||||
with 'make installcheck'.
|
||||
|
||||
The configuration files are now processed using a deterministic order.
|
||||
|
||||
The test suite code coverage for mcron modules is now at 66.8% in term of
|
||||
number of lines (mcron-1.1 was at 23.7%).
|
||||
|
||||
* Noteworthy changes in release 1.1 (2018-03-19) [stable]
|
||||
|
||||
** New features
|
||||
|
||||
The 'job' procedure has now a '#:user' keyword argument which allows
|
||||
specifying a different user that will run it.
|
||||
|
||||
Additional man pages for 'cron(8)' and 'crontab(1)' are now generated using
|
||||
GNU Help2man.
|
||||
|
||||
** Bug fixes
|
||||
|
||||
Child process created when executing a job are now properly cleaned even
|
||||
when execution fails by using 'dynamic-wind' construct.
|
||||
|
||||
** Improvements
|
||||
|
||||
GNU Guile 2.2 is now supported.
|
||||
|
||||
Some procedures are now written using functional style and include a
|
||||
docstring. 'def-macro' usages are now replaced with hygienic macros.
|
||||
|
||||
Compilation is now done using a non-recursive Makefile, supports out of tree
|
||||
builds, and use silent rules by default.
|
||||
|
||||
Guile object files creation don't rely on auto-compilation anymore and are
|
||||
installed in 'site-ccache' directory.
|
||||
|
||||
Jobs are now internally represented using SRFI-9 records instead of vectors.
|
||||
|
||||
Changelog are generated from Git logs when generating the tarball using
|
||||
Gnulib gitlog-to-changelog script.
|
||||
|
||||
A test suite is now available and can be run with 'make check'.
|
||||
|
||||
** Changes in behavior
|
||||
|
||||
The "--enable-debug" configure variable has been removed and replaced with
|
||||
MCRON_DEBUG environment variable.
|
||||
|
||||
The "--disable-multi-user" configure variable is now used to not build and
|
||||
install the 'cron' and 'crontab' programs. It has replaced the
|
||||
"--enable-no-vixie-clobber" which had similar effect.
|
||||
|
||||
(mcron core) module is now deprecated and has been superseeded by
|
||||
(mcron base).
|
||||
|
||||
* Noteworthy changes in release 1.0.8 (2014-04-28) [stable]
|
||||
|
||||
Man page is now generated with GNU Help2man before installation and
|
||||
distributed in the tarball.
|
||||
|
||||
* Noteworthy changes in release 1.0.7 (2012-02-04) [stable]
|
||||
|
||||
Mcron is now compatible with Guile 2.0.
|
||||
|
||||
FreeDesktop.org's standard user configuration directories are now used to
|
||||
find the user script files.
|
||||
|
||||
* Noteworthy changes in release 1.0.6 (2010-06-20) [stable]
|
||||
|
||||
The copyright notices are now standardized on all auxiliary files. This
|
||||
follows the example set by the GNU hello program.
|
||||
|
||||
immutable end texts from the texinfo document are now removed, to
|
||||
accomodate with Debian requirements.
|
||||
|
||||
* Noteworthy changes in release 1.0.5 (2010-06-13) [stable]
|
||||
|
||||
Some technical changes to the build system has been made to help the
|
||||
distribution to Debian.
|
||||
|
||||
The Git repository has been completely re-hashed, and now represents a
|
||||
complete and faithful history of the package's development since its
|
||||
inception.
|
||||
|
||||
* Noteworthy changes in release 1.0.4 (2008-02-21) [stable]
|
||||
|
||||
The source code is now held in a Git repository, which can be checked-out at
|
||||
<git://git.savannah.gnu.org/mcron.git>.
|
||||
|
||||
The code is now covered by the GPLv3 license.
|
||||
|
||||
* Noteworthy changes in release 1.0.3 (2006-04-16) [stable]
|
||||
|
||||
daylight savings time shifts are now properly handled
|
||||
|
||||
Parsing Vixie-style input files has been improved.
|
||||
|
||||
Crontab entries can now be corrected instead of just wiping out the file.
|
||||
|
||||
Mcron is now compatible with Guile 1.8.
|
||||
|
||||
The manual is now licensed under the GNU Free Documentation License (GFDL)
|
||||
|
||||
* Noteworthy changes in release 1.0.2 (2006-01-02) [stable]
|
||||
|
||||
* Noteworthy changes in release 1.0.1 (2004-05-15) [stable]
|
||||
|
||||
The mailing list <bug-mcron@gnu.org> has been set-up.
|
||||
|
||||
* Noteworthy changes in release 1.0 (2003-12-12) [stable]
|
||||
|
||||
Mcron is now officially a GNU program.
|
||||
|
||||
* Noteworthy changes in release 0.99.3 (2003-08-05) [stable]
|
||||
|
||||
The code is now splitted into modules.
|
||||
|
||||
* Noteworthy changes in release 0.99.2 (2003-07-20) [stable]
|
||||
|
||||
The implementation is now really 100% Vixie compatible.
|
||||
|
||||
Some Guile limitations such as the absence of POSIX threads and signals has
|
||||
been worked around.
|
||||
|
||||
* Noteworthy changes in release 0.99.1 (2003-07-05) [stable]
|
||||
|
||||
Installation of cron and crontab is now disabled by default (suspect problems
|
||||
with Guile internals are preventing these from working properly).
|
||||
|
||||
The project is now managed on Savannah. A CVS repository and web page have been
|
||||
created.
|
||||
|
||||
========================================================================
|
||||
|
||||
Copyright © 2003, 2005, 2006 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
Copyright © 2017, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
Copyright © 2018 宋文武 <iyzsong@member.fsf.org>
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved.
|
||||
69
README
69
README
|
|
@ -1,69 +0,0 @@
|
|||
This is GNU Mcron, a tool to run jobs at scheduled times. It is a complete
|
||||
replacement for Vixie cron. Besides supporting the traditional Vixie syntax
|
||||
for its configuration files, GNU Mcron offers the possibility to define jobs
|
||||
using the Scheme language.
|
||||
|
||||
See the INSTALL file for generic information about how to configure and
|
||||
install GNU Mcron. If this file is not present, see HACKING for
|
||||
preliminary build instructions.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
IMPORTANT NOTICES
|
||||
|
||||
Do not (yet) install this software on a machine which relies for its
|
||||
functioning on its current set of crontabs.
|
||||
|
||||
To not replace the cron daemon on a system, the package must be installed
|
||||
with the --disable-multi-user configure option.
|
||||
|
||||
Before installing this package for the first time, it is necessary to terminate
|
||||
any running cron daemons on your system. If your old cron is not Vixie or
|
||||
accurately Vixie compatible (files in /var/cron/tabs*, /var/cron/allow,
|
||||
/var/cron/deny, /etc/crontab, /var/run/cron.pid) then you will need to clear out
|
||||
all old crontabs and make new ones afresh - or else look very carefully at the
|
||||
options you pass to the package configure script, as follows.
|
||||
|
||||
It is often the case that GNU/Linux distributions and other Unices hacked the
|
||||
cron daemon to use different directories to those above. You can use configure
|
||||
options --spool-dir, --socket-file, --allow-file, --deny-file, --pid-file and
|
||||
--tmp-dir to make mcron behave similarly. Note that, with the exception of
|
||||
tmp-dir, none of these files or directories should be accessible by ordinary
|
||||
(non-root) users.
|
||||
|
||||
If your old cron is Vixie, or very similar, mcron should fall right into place
|
||||
where your old cron was (the binaries cron and crontab will be replaced, but if
|
||||
your existing system has a binary called crond, you should make this a link
|
||||
to mcron), and you should be able to continue to use your existing crontabs
|
||||
without noticing any changes.
|
||||
|
||||
If you don't want to clobber your existing cron executables, you can specify
|
||||
the --program-prefix option to configure with a prefix ending in a
|
||||
non-alphabetic character, for example "m.", and then run the programs as
|
||||
m.mcron, m.cron (or m.crond) and m.crontab.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
|
||||
After compilation, read the info file for full instructions for use (typing
|
||||
'info -f doc/mcron.info' at the command line should suffice). Notes for end
|
||||
users, sysadmins, and developers who wish to incorporate mcron into their own
|
||||
programs are included here.
|
||||
|
||||
Features which might be implemented sometime sooner or later are noted in the
|
||||
TODO file.
|
||||
|
||||
Please send all other bug reports to bug-mcron@gnu.org.
|
||||
|
||||
Mcron is free software. See the file COPYING for copying conditions.
|
||||
|
||||
The mcron development home page is at http://www.gnu.org/software/mcron, and it
|
||||
can be obtained from ftp://ftp.gnu.org/pub/gnu/mcron.
|
||||
|
||||
-----
|
||||
|
||||
Copyright © 2003, 2005, 2006, 2012, 2014 Dale Mellor
|
||||
Copyright © 2018 Mathieu Lirzin
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. This file is offered as-is,
|
||||
without warranty of any kind.
|
||||
54
TODO
54
TODO
|
|
@ -1,54 +0,0 @@
|
|||
GNU mcron --- TODO -*-text-*-
|
||||
|
||||
Copyright (C) 2015, 2016 Mathieu Lirzin
|
||||
Copyright (C) 2003, 2005, 2006, 2014 Dale Mellor
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved.
|
||||
|
||||
|
||||
|
||||
Maybe in the near future...
|
||||
|
||||
* Logging.
|
||||
|
||||
* Check POSIX compliance (should be okay if Vixie cron was okay).
|
||||
|
||||
* Work out how to give each user his own closure (or environment or module
|
||||
or continuation) for his configuration files so that he can't mess the
|
||||
core or other users' files up. Then allow scheme code in the system
|
||||
crontabs.
|
||||
|
||||
* Provide a test suite using SRFI-64 API.
|
||||
<http://srfi.schemers.org/srfi-64/srfi-64.html>.
|
||||
|
||||
* Internationalize Mcron using GNU Gettext and ask the Translation
|
||||
Project to handle the localization.
|
||||
|
||||
|
||||
There are no plans to actually do the following any time soon...
|
||||
|
||||
* Develop at and batch modes of operation.
|
||||
|
||||
* Make compatibilities with other crons (BSD, SYSV, Solaris, Dillon's, ...)
|
||||
|
||||
* Port to BSD, other operating systems.
|
||||
|
||||
* Full security audit for Vixie mode.
|
||||
|
||||
|
||||
|
||||
May happen if version 2.0 ever materializes...
|
||||
|
||||
* UNIX or TCP socket will allow interrogation and control of a running
|
||||
daemon (unrelated to, or maybe a major enhancement of, socket used for
|
||||
communication from crontab process).
|
||||
|
||||
* Add anacron functionality (run missed jobs if the daemon is stopped, for
|
||||
example if a personal computer does not run 24 hours a day).
|
||||
|
||||
* TCP socket to allow control via HTTP (web browser interface). Or maybe
|
||||
crontab-like CGI personality.
|
||||
|
||||
* GTK+/d-bus/Gnome3 interface.
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Initialize the build system.
|
||||
|
||||
set -e -x
|
||||
exec autoreconf -vfi
|
||||
|
|
@ -1,557 +0,0 @@
|
|||
eval '(exit $?0)' && eval 'exec perl -wS "$0" "$@"'
|
||||
& eval 'exec perl -wS "$0" $argv:q'
|
||||
if 0;
|
||||
# Generate a release announcement message.
|
||||
|
||||
my $VERSION = '2018-03-07 03:46'; # UTC
|
||||
# The definition above must lie within the first 8 lines in order
|
||||
# for the Emacs time-stamp write hook (at end) to update it.
|
||||
# If you change this file with Emacs, please let the write hook
|
||||
# do its job. Otherwise, update this string manually.
|
||||
|
||||
# Copyright (C) 2002-2018 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Written by Jim Meyering
|
||||
|
||||
use strict;
|
||||
|
||||
use Getopt::Long;
|
||||
use POSIX qw(strftime);
|
||||
|
||||
(my $ME = $0) =~ s|.*/||;
|
||||
|
||||
my %valid_release_types = map {$_ => 1} qw (alpha beta stable);
|
||||
my @archive_suffixes = ('tar.gz', 'tar.bz2', 'tar.lzma', 'tar.xz');
|
||||
my %digest_classes =
|
||||
(
|
||||
'md5' => (eval { require Digest::MD5; } and 'Digest::MD5'),
|
||||
'sha1' => ((eval { require Digest::SHA; } and 'Digest::SHA')
|
||||
or (eval { require Digest::SHA1; } and 'Digest::SHA1'))
|
||||
);
|
||||
my $srcdir = '.';
|
||||
|
||||
sub usage ($)
|
||||
{
|
||||
my ($exit_code) = @_;
|
||||
my $STREAM = ($exit_code == 0 ? *STDOUT : *STDERR);
|
||||
if ($exit_code != 0)
|
||||
{
|
||||
print $STREAM "Try '$ME --help' for more information.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
my @types = sort keys %valid_release_types;
|
||||
print $STREAM <<EOF;
|
||||
Usage: $ME [OPTIONS]
|
||||
Generate an announcement message. Run this from builddir.
|
||||
|
||||
OPTIONS:
|
||||
|
||||
These options must be specified:
|
||||
|
||||
--release-type=TYPE TYPE must be one of @types
|
||||
--package-name=PACKAGE_NAME
|
||||
--previous-version=VER
|
||||
--current-version=VER
|
||||
--gpg-key-id=ID The GnuPG ID of the key used to sign the tarballs
|
||||
--url-directory=URL_DIR
|
||||
|
||||
The following are optional:
|
||||
|
||||
--news=NEWS_FILE include the NEWS section about this release
|
||||
from this NEWS_FILE; accumulates.
|
||||
--srcdir=DIR where to find the NEWS_FILEs (default: $srcdir)
|
||||
--bootstrap-tools=TOOL_LIST a comma-separated list of tools, e.g.,
|
||||
autoconf,automake,bison,gnulib
|
||||
--gnulib-version=VERSION report VERSION as the gnulib version, where
|
||||
VERSION is the result of running git describe
|
||||
in the gnulib source directory.
|
||||
required if gnulib is in TOOL_LIST.
|
||||
--no-print-checksums do not emit MD5 or SHA1 checksums
|
||||
--archive-suffix=SUF add SUF to the list of archive suffixes
|
||||
--mail-headers=HEADERS a space-separated list of mail headers, e.g.,
|
||||
To: x\@example.com Cc: y-announce\@example.com,...
|
||||
|
||||
--help display this help and exit
|
||||
--version output version information and exit
|
||||
|
||||
EOF
|
||||
}
|
||||
exit $exit_code;
|
||||
}
|
||||
|
||||
|
||||
=item C<%size> = C<sizes (@file)>
|
||||
|
||||
Compute the sizes of the C<@file> and return them as a hash. Return
|
||||
C<undef> if one of the computation failed.
|
||||
|
||||
=cut
|
||||
|
||||
sub sizes (@)
|
||||
{
|
||||
my (@file) = @_;
|
||||
|
||||
my $fail = 0;
|
||||
my %res;
|
||||
foreach my $f (@file)
|
||||
{
|
||||
my $cmd = "du -h $f";
|
||||
my $t = `$cmd`;
|
||||
# FIXME-someday: give a better diagnostic, a la $PROCESS_STATUS
|
||||
$@
|
||||
and (warn "command failed: '$cmd'\n"), $fail = 1;
|
||||
chomp $t;
|
||||
$t =~ s/^\s*([\d.]+[MkK]).*/${1}B/;
|
||||
$res{$f} = $t;
|
||||
}
|
||||
return $fail ? undef : %res;
|
||||
}
|
||||
|
||||
=item C<print_locations ($title, \@url, \%size, @file)
|
||||
|
||||
Print a section C<$title> dedicated to the list of <@file>, which
|
||||
sizes are stored in C<%size>, and which are available from the C<@url>.
|
||||
|
||||
=cut
|
||||
|
||||
sub print_locations ($\@\%@)
|
||||
{
|
||||
my ($title, $url, $size, @file) = @_;
|
||||
print "Here are the $title:\n";
|
||||
foreach my $url (@{$url})
|
||||
{
|
||||
for my $file (@file)
|
||||
{
|
||||
print " $url/$file";
|
||||
print " (", $$size{$file}, ")"
|
||||
if exists $$size{$file};
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
=item C<print_checksums (@file)
|
||||
|
||||
Print the MD5 and SHA1 signature section for each C<@file>.
|
||||
|
||||
=cut
|
||||
|
||||
sub print_checksums (@)
|
||||
{
|
||||
my (@file) = @_;
|
||||
|
||||
print "Here are the MD5 and SHA1 checksums:\n";
|
||||
print "\n";
|
||||
|
||||
foreach my $meth (qw (md5 sha1))
|
||||
{
|
||||
my $class = $digest_classes{$meth} or next;
|
||||
foreach my $f (@file)
|
||||
{
|
||||
open IN, '<', $f
|
||||
or die "$ME: $f: cannot open for reading: $!\n";
|
||||
binmode IN;
|
||||
my $dig = $class->new->addfile(*IN)->hexdigest;
|
||||
close IN;
|
||||
print "$dig $f\n";
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
|
||||
=item C<print_news_deltas ($news_file, $prev_version, $curr_version)
|
||||
|
||||
Print the section of the NEWS file C<$news_file> addressing changes
|
||||
between versions C<$prev_version> and C<$curr_version>.
|
||||
|
||||
=cut
|
||||
|
||||
sub print_news_deltas ($$$)
|
||||
{
|
||||
my ($news_file, $prev_version, $curr_version) = @_;
|
||||
|
||||
my $news_name = $news_file;
|
||||
$news_name =~ s|^\Q$srcdir\E/||;
|
||||
|
||||
print "\n$news_name\n\n";
|
||||
|
||||
# Print all lines from $news_file, starting with the first one
|
||||
# that mentions $curr_version up to but not including
|
||||
# the first occurrence of $prev_version.
|
||||
my $in_items;
|
||||
|
||||
my $re_prefix = qr/(?:\* )?(?:Noteworthy c|Major c|C)(?i:hanges)/;
|
||||
|
||||
my $found_news;
|
||||
open NEWS, '<', $news_file
|
||||
or die "$ME: $news_file: cannot open for reading: $!\n";
|
||||
while (defined (my $line = <NEWS>))
|
||||
{
|
||||
if ( ! $in_items)
|
||||
{
|
||||
# Match lines like these:
|
||||
# * Major changes in release 5.0.1:
|
||||
# * Noteworthy changes in release 6.6 (2006-11-22) [stable]
|
||||
$line =~ /^$re_prefix.*(?:[^\d.]|$)\Q$curr_version\E(?:[^\d.]|$)/o
|
||||
or next;
|
||||
$in_items = 1;
|
||||
print $line;
|
||||
}
|
||||
else
|
||||
{
|
||||
# This regexp must not match version numbers in NEWS items.
|
||||
# For example, they might well say "introduced in 4.5.5",
|
||||
# and we don't want that to match.
|
||||
$line =~ /^$re_prefix.*(?:[^\d.]|$)\Q$prev_version\E(?:[^\d.]|$)/o
|
||||
and last;
|
||||
print $line;
|
||||
$line =~ /\S/
|
||||
and $found_news = 1;
|
||||
}
|
||||
}
|
||||
close NEWS;
|
||||
|
||||
$in_items
|
||||
or die "$ME: $news_file: no matching lines for '$curr_version'\n";
|
||||
$found_news
|
||||
or die "$ME: $news_file: no news item found for '$curr_version'\n";
|
||||
}
|
||||
|
||||
sub print_changelog_deltas ($$)
|
||||
{
|
||||
my ($package_name, $prev_version) = @_;
|
||||
|
||||
# Print new ChangeLog entries.
|
||||
|
||||
# First find all CVS-controlled ChangeLog files.
|
||||
use File::Find;
|
||||
my @changelog;
|
||||
find ({wanted => sub {$_ eq 'ChangeLog' && -d 'CVS'
|
||||
and push @changelog, $File::Find::name}},
|
||||
'.');
|
||||
|
||||
# If there are no ChangeLog files, we're done.
|
||||
@changelog
|
||||
or return;
|
||||
my %changelog = map {$_ => 1} @changelog;
|
||||
|
||||
# Reorder the list of files so that if there are ChangeLog
|
||||
# files in the specified directories, they're listed first,
|
||||
# in this order:
|
||||
my @dir = qw ( . src lib m4 config doc );
|
||||
|
||||
# A typical @changelog array might look like this:
|
||||
# ./ChangeLog
|
||||
# ./po/ChangeLog
|
||||
# ./m4/ChangeLog
|
||||
# ./lib/ChangeLog
|
||||
# ./doc/ChangeLog
|
||||
# ./config/ChangeLog
|
||||
my @reordered;
|
||||
foreach my $d (@dir)
|
||||
{
|
||||
my $dot_slash = $d eq '.' ? $d : "./$d";
|
||||
my $target = "$dot_slash/ChangeLog";
|
||||
delete $changelog{$target}
|
||||
and push @reordered, $target;
|
||||
}
|
||||
|
||||
# Append any remaining ChangeLog files.
|
||||
push @reordered, sort keys %changelog;
|
||||
|
||||
# Remove leading './'.
|
||||
@reordered = map { s!^\./!!; $_ } @reordered;
|
||||
|
||||
print "\nChangeLog entries:\n\n";
|
||||
# print join ("\n", @reordered), "\n";
|
||||
|
||||
$prev_version =~ s/\./_/g;
|
||||
my $prev_cvs_tag = "\U$package_name\E-$prev_version";
|
||||
|
||||
my $cmd = "cvs -n diff -u -r$prev_cvs_tag -rHEAD @reordered";
|
||||
open DIFF, '-|', $cmd
|
||||
or die "$ME: cannot run '$cmd': $!\n";
|
||||
# Print two types of lines, making minor changes:
|
||||
# Lines starting with '+++ ', e.g.,
|
||||
# +++ ChangeLog 22 Feb 2003 16:52:51 -0000 1.247
|
||||
# and those starting with '+'.
|
||||
# Don't print the others.
|
||||
my $prev_printed_line_empty = 1;
|
||||
while (defined (my $line = <DIFF>))
|
||||
{
|
||||
if ($line =~ /^\+\+\+ /)
|
||||
{
|
||||
my $separator = "*"x70 ."\n";
|
||||
$line =~ s///;
|
||||
$line =~ s/\s.*//;
|
||||
$prev_printed_line_empty
|
||||
or print "\n";
|
||||
print $separator, $line, $separator;
|
||||
}
|
||||
elsif ($line =~ /^\+/)
|
||||
{
|
||||
$line =~ s///;
|
||||
print $line;
|
||||
$prev_printed_line_empty = ($line =~ /^$/);
|
||||
}
|
||||
}
|
||||
close DIFF;
|
||||
|
||||
# The exit code should be 1.
|
||||
# Allow in case there are no modified ChangeLog entries.
|
||||
$? == 256 || $? == 128
|
||||
or warn "warning: '$cmd' had unexpected exit code or signal ($?)\n";
|
||||
}
|
||||
|
||||
sub get_tool_versions ($$)
|
||||
{
|
||||
my ($tool_list, $gnulib_version) = @_;
|
||||
@$tool_list
|
||||
or return ();
|
||||
|
||||
my $fail;
|
||||
my @tool_version_pair;
|
||||
foreach my $t (@$tool_list)
|
||||
{
|
||||
if ($t eq 'gnulib')
|
||||
{
|
||||
push @tool_version_pair, ucfirst $t . ' ' . $gnulib_version;
|
||||
next;
|
||||
}
|
||||
# Assume that the last "word" on the first line of
|
||||
# 'tool --version' output is the version string.
|
||||
my ($first_line, undef) = split ("\n", `$t --version`);
|
||||
if ($first_line =~ /.* (\d[\w.-]+)$/)
|
||||
{
|
||||
$t = ucfirst $t;
|
||||
push @tool_version_pair, "$t $1";
|
||||
}
|
||||
else
|
||||
{
|
||||
defined $first_line
|
||||
and $first_line = '';
|
||||
warn "$t: unexpected --version output\n:$first_line";
|
||||
$fail = 1;
|
||||
}
|
||||
}
|
||||
|
||||
$fail
|
||||
and exit 1;
|
||||
|
||||
return @tool_version_pair;
|
||||
}
|
||||
|
||||
{
|
||||
# Neutralize the locale, so that, for instance, "du" does not
|
||||
# issue "1,2" instead of "1.2", what confuses our regexps.
|
||||
$ENV{LC_ALL} = "C";
|
||||
|
||||
my $mail_headers;
|
||||
my $release_type;
|
||||
my $package_name;
|
||||
my $prev_version;
|
||||
my $curr_version;
|
||||
my $gpg_key_id;
|
||||
my @url_dir_list;
|
||||
my @news_file;
|
||||
my $bootstrap_tools;
|
||||
my $gnulib_version;
|
||||
my $print_checksums_p = 1;
|
||||
|
||||
# Reformat the warnings before displaying them.
|
||||
local $SIG{__WARN__} = sub
|
||||
{
|
||||
my ($msg) = @_;
|
||||
# Warnings from GetOptions.
|
||||
$msg =~ s/Option (\w)/option --$1/;
|
||||
warn "$ME: $msg";
|
||||
};
|
||||
|
||||
GetOptions
|
||||
(
|
||||
'mail-headers=s' => \$mail_headers,
|
||||
'release-type=s' => \$release_type,
|
||||
'package-name=s' => \$package_name,
|
||||
'previous-version=s' => \$prev_version,
|
||||
'current-version=s' => \$curr_version,
|
||||
'gpg-key-id=s' => \$gpg_key_id,
|
||||
'url-directory=s' => \@url_dir_list,
|
||||
'news=s' => \@news_file,
|
||||
'srcdir=s' => \$srcdir,
|
||||
'bootstrap-tools=s' => \$bootstrap_tools,
|
||||
'gnulib-version=s' => \$gnulib_version,
|
||||
'print-checksums!' => \$print_checksums_p,
|
||||
'archive-suffix=s' => \@archive_suffixes,
|
||||
|
||||
help => sub { usage 0 },
|
||||
version => sub { print "$ME version $VERSION\n"; exit },
|
||||
) or usage 1;
|
||||
|
||||
my $fail = 0;
|
||||
# Ensure that each required option is specified.
|
||||
$release_type
|
||||
or (warn "release type not specified\n"), $fail = 1;
|
||||
$package_name
|
||||
or (warn "package name not specified\n"), $fail = 1;
|
||||
$prev_version
|
||||
or (warn "previous version string not specified\n"), $fail = 1;
|
||||
$curr_version
|
||||
or (warn "current version string not specified\n"), $fail = 1;
|
||||
$gpg_key_id
|
||||
or (warn "GnuPG key ID not specified\n"), $fail = 1;
|
||||
@url_dir_list
|
||||
or (warn "URL directory name(s) not specified\n"), $fail = 1;
|
||||
|
||||
my @tool_list = split ',', $bootstrap_tools
|
||||
if $bootstrap_tools;
|
||||
|
||||
grep (/^gnulib$/, @tool_list) ^ defined $gnulib_version
|
||||
and (warn "when specifying gnulib as a tool, you must also specify\n"
|
||||
. "--gnulib-version=V, where V is the result of running git describe\n"
|
||||
. "in the gnulib source directory.\n"), $fail = 1;
|
||||
|
||||
!$release_type || exists $valid_release_types{$release_type}
|
||||
or (warn "'$release_type': invalid release type\n"), $fail = 1;
|
||||
|
||||
@ARGV
|
||||
and (warn "too many arguments:\n", join ("\n", @ARGV), "\n"),
|
||||
$fail = 1;
|
||||
$fail
|
||||
and usage 1;
|
||||
|
||||
my $my_distdir = "$package_name-$curr_version";
|
||||
|
||||
my $xd = "$package_name-$prev_version-$curr_version.xdelta";
|
||||
|
||||
my @candidates = map { "$my_distdir.$_" } @archive_suffixes;
|
||||
my @tarballs = grep {-f $_} @candidates;
|
||||
|
||||
@tarballs
|
||||
or die "$ME: none of " . join(', ', @candidates) . " were found\n";
|
||||
my @sizable = @tarballs;
|
||||
-f $xd
|
||||
and push @sizable, $xd;
|
||||
my %size = sizes (@sizable);
|
||||
%size
|
||||
or exit 1;
|
||||
|
||||
my $headers = '';
|
||||
if (defined $mail_headers)
|
||||
{
|
||||
($headers = $mail_headers) =~ s/\s+(\S+:)/\n$1/g;
|
||||
$headers .= "\n";
|
||||
}
|
||||
|
||||
# The markup is escaped as <\# so that when this script is sent by
|
||||
# mail (or part of a diff), Gnus is not triggered.
|
||||
print <<EOF;
|
||||
|
||||
${headers}Subject: $my_distdir released [$release_type]
|
||||
|
||||
<\#secure method=pgpmime mode=sign>
|
||||
|
||||
FIXME: put comments here
|
||||
|
||||
EOF
|
||||
|
||||
if (@url_dir_list == 1 && @tarballs == 1)
|
||||
{
|
||||
# When there's only one tarball and one URL, use a more concise form.
|
||||
my $m = "$url_dir_list[0]/$tarballs[0]";
|
||||
print "Here are the compressed sources and a GPG detached signature[*]:\n"
|
||||
. " $m\n"
|
||||
. " $m.sig\n\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print_locations ("compressed sources", @url_dir_list, %size, @tarballs);
|
||||
-f $xd
|
||||
and print_locations ("xdelta diffs (useful? if so, "
|
||||
. "please tell bug-gnulib\@gnu.org)",
|
||||
@url_dir_list, %size, $xd);
|
||||
my @sig_files = map { "$_.sig" } @tarballs;
|
||||
print_locations ("GPG detached signatures[*]", @url_dir_list, %size,
|
||||
@sig_files);
|
||||
}
|
||||
|
||||
if ($url_dir_list[0] =~ "gnu\.org")
|
||||
{
|
||||
print "Use a mirror for higher download bandwidth:\n";
|
||||
if (@tarballs == 1 && $url_dir_list[0] =~ m!https://ftp\.gnu\.org/gnu/!)
|
||||
{
|
||||
(my $m = "$url_dir_list[0]/$tarballs[0]")
|
||||
=~ s!https://ftp\.gnu\.org/gnu/!https://ftpmirror\.gnu\.org/!;
|
||||
print " $m\n"
|
||||
. " $m.sig\n\n";
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
print " https://www.gnu.org/order/ftp.html\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
$print_checksums_p
|
||||
and print_checksums (@sizable);
|
||||
|
||||
print <<EOF;
|
||||
[*] Use a .sig file to verify that the corresponding file (without the
|
||||
.sig suffix) is intact. First, be sure to download both the .sig file
|
||||
and the corresponding tarball. Then, run a command like this:
|
||||
|
||||
gpg --verify $tarballs[0].sig
|
||||
|
||||
If that command fails because you don't have the required public key,
|
||||
then run this command to import it:
|
||||
|
||||
gpg --keyserver keys.gnupg.net --recv-keys $gpg_key_id
|
||||
|
||||
and rerun the 'gpg --verify' command.
|
||||
EOF
|
||||
|
||||
my @tool_versions = get_tool_versions (\@tool_list, $gnulib_version);
|
||||
@tool_versions
|
||||
and print "\nThis release was bootstrapped with the following tools:",
|
||||
join ('', map {"\n $_"} @tool_versions), "\n";
|
||||
|
||||
print_news_deltas ($_, $prev_version, $curr_version)
|
||||
foreach @news_file;
|
||||
|
||||
$release_type eq 'stable'
|
||||
or print_changelog_deltas ($package_name, $prev_version);
|
||||
|
||||
exit 0;
|
||||
}
|
||||
|
||||
### Setup "GNU" style for perl-mode and cperl-mode.
|
||||
## Local Variables:
|
||||
## mode: perl
|
||||
## perl-indent-level: 2
|
||||
## perl-continued-statement-offset: 2
|
||||
## perl-continued-brace-offset: 0
|
||||
## perl-brace-offset: 0
|
||||
## perl-brace-imaginary-offset: 0
|
||||
## perl-label-offset: -2
|
||||
## perl-extra-newline-before-brace: t
|
||||
## perl-merge-trailing-else: nil
|
||||
## eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
## time-stamp-start: "my $VERSION = '"
|
||||
## time-stamp-format: "%:y-%02m-%02d %02H:%02M"
|
||||
## time-stamp-time-zone: "UTC0"
|
||||
## time-stamp-end: "'; # UTC"
|
||||
## End:
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
#!/bin/sh
|
||||
# In a git/autoconf/automake-enabled project with a NEWS file and a version-
|
||||
# controlled .prev-version file, automate the procedure by which we record
|
||||
# the date, release-type and version string in the NEWS file. That commit
|
||||
# will serve to identify the release, so apply a signed tag to it as well.
|
||||
VERSION=2018-03-07.03 # UTC
|
||||
|
||||
# Note: this is a bash script (could be zsh or dash)
|
||||
|
||||
# Copyright (C) 2009-2018 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Written by Jim Meyering
|
||||
|
||||
ME=$(basename "$0")
|
||||
warn() { printf '%s: %s\n' "$ME" "$*" >&2; }
|
||||
die() { warn "$*"; exit 1; }
|
||||
|
||||
help()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $ME [OPTION...] VERSION RELEASE_TYPE
|
||||
|
||||
Run this script from top_srcdir to perform the final pre-release NEWS
|
||||
update in which the date, release-type and version string are
|
||||
recorded. Commit that result with a log entry marking the release,
|
||||
and apply a signed tag. Run it from your project's top-level
|
||||
directory.
|
||||
|
||||
Requirements:
|
||||
- you use git for version-control
|
||||
- a version-controlled .prev-version file
|
||||
- a NEWS file, with line 3 identical to this:
|
||||
$noteworthy_stub
|
||||
|
||||
Options:
|
||||
--branch=BRANCH set release branch (default: $branch)
|
||||
-C, --builddir=DIR location of (configured) Makefile (default: $builddir)
|
||||
--help print this help, then exit
|
||||
--version print version number, then exit
|
||||
|
||||
EXAMPLE:
|
||||
To update NEWS and tag the beta 8.1 release of coreutils, I would run this:
|
||||
|
||||
$ME 8.1 beta
|
||||
|
||||
Report bugs and patches to <bug-gnulib@gnu.org>.
|
||||
EOF
|
||||
exit
|
||||
}
|
||||
|
||||
version()
|
||||
{
|
||||
year=$(echo "$VERSION" | sed 's/[^0-9].*//')
|
||||
cat <<EOF
|
||||
$ME $VERSION
|
||||
Copyright (C) $year Free Software Foundation, Inc,
|
||||
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law.
|
||||
EOF
|
||||
exit
|
||||
}
|
||||
|
||||
## ------ ##
|
||||
## Main. ##
|
||||
## ------ ##
|
||||
|
||||
# Constants.
|
||||
noteworthy='* Noteworthy changes in release'
|
||||
noteworthy_stub="$noteworthy ?.? (????-??-??) [?]"
|
||||
|
||||
# Variables.
|
||||
branch=$(git branch | sed -ne '/^\* /{s///;p;q;}')
|
||||
builddir=.
|
||||
|
||||
while test $# != 0
|
||||
do
|
||||
# Handle --option=value by splitting apart and putting back on argv.
|
||||
case $1 in
|
||||
--*=*)
|
||||
opt=$(echo "$1" | sed -e 's/=.*//')
|
||||
val=$(echo "$1" | sed -e 's/[^=]*=//')
|
||||
shift
|
||||
set dummy "$opt" "$val" "$@"; shift
|
||||
;;
|
||||
esac
|
||||
|
||||
case $1 in
|
||||
--help|--version) ${1#--};;
|
||||
--branch) shift; branch=$1; shift ;;
|
||||
-C|--builddir) shift; builddir=$1; shift ;;
|
||||
--*) die "unrecognized option: $1";;
|
||||
*) break;;
|
||||
esac
|
||||
done
|
||||
|
||||
test $# = 2 \
|
||||
|| die "Usage: $ME [OPTION...] VERSION TYPE"
|
||||
|
||||
ver=$1
|
||||
type=$2
|
||||
|
||||
|
||||
## ---------------------- ##
|
||||
## First, sanity checks. ##
|
||||
## ---------------------- ##
|
||||
|
||||
# Verify that $ver looks like a version number, and...
|
||||
echo "$ver"|grep -E '^[0-9][0-9.]*[0-9]$' > /dev/null \
|
||||
|| die "invalid version: $ver"
|
||||
prev_ver=$(cat .prev-version) \
|
||||
|| die 'failed to determine previous version number from .prev-version'
|
||||
|
||||
# Verify that $ver is sensible (> .prev-version).
|
||||
case $(printf "$prev_ver\n$ver\n"|sort -V -u|tr '\n' ':') in
|
||||
"$prev_ver:$ver:") ;;
|
||||
*) die "invalid version: $ver (<= $prev_ver)";;
|
||||
esac
|
||||
|
||||
case $type in
|
||||
alpha|beta|stable) ;;
|
||||
*) die "invalid release type: $type";;
|
||||
esac
|
||||
|
||||
# No local modifications allowed.
|
||||
case $(git diff-index --name-only HEAD) in
|
||||
'') ;;
|
||||
*) die 'this tree is dirty; commit your changes first';;
|
||||
esac
|
||||
|
||||
# Ensure the current branch name is correct:
|
||||
curr_br=$(git rev-parse --symbolic-full-name HEAD)
|
||||
test "$curr_br" = "refs/heads/$branch" || die not on branch $branch
|
||||
|
||||
# Extract package name from Makefile.
|
||||
Makefile=$builddir/Makefile
|
||||
pkg=$(sed -n 's/^PACKAGE = \(.*\)/\1/p' "$Makefile") \
|
||||
|| die "failed to determine package name from $Makefile"
|
||||
|
||||
# Check that line 3 of NEWS is the stub line about to be replaced.
|
||||
test "$(sed -n 3p NEWS)" = "$noteworthy_stub" \
|
||||
|| die "line 3 of NEWS must be exactly '$noteworthy_stub'"
|
||||
|
||||
## --------------- ##
|
||||
## Then, changes. ##
|
||||
## --------------- ##
|
||||
|
||||
# Update NEWS to have today's date, plus desired version number and $type.
|
||||
perl -MPOSIX -ni -e 'my $today = strftime "%F", localtime time;' \
|
||||
-e 'my ($type, $ver) = qw('"$type $ver"');' \
|
||||
-e 'my $pfx = "'"$noteworthy"'";' \
|
||||
-e 'print $.==3 ? "$pfx $ver ($today) [$type]\n" : $_' \
|
||||
NEWS || die 'failed to update NEWS'
|
||||
|
||||
printf "version $ver\n\n* NEWS: Record release date.\n" \
|
||||
| git commit -F - -a || die 'git commit failed'
|
||||
git tag -s -m "$pkg $ver" v$ver HEAD || die 'git tag failed'
|
||||
|
||||
# Local variables:
|
||||
# indent-tabs-mode: nil
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "VERSION="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: " # UTC"
|
||||
# End:
|
||||
|
|
@ -1,210 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Run this after each non-alpha release, to update the web documentation at
|
||||
# https://www.gnu.org/software/$pkg/manual/
|
||||
|
||||
VERSION=2018-03-07.03; # UTC
|
||||
|
||||
# Copyright (C) 2009-2018 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
ME=$(basename "$0")
|
||||
warn() { printf '%s: %s\n' "$ME" "$*" >&2; }
|
||||
die() { warn "$*"; exit 1; }
|
||||
|
||||
help()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $ME
|
||||
|
||||
Run this script from top_srcdir (no arguments) after each non-alpha
|
||||
release, to update the web documentation at
|
||||
https://www.gnu.org/software/\$pkg/manual/
|
||||
|
||||
This script assumes you're using git for revision control, and
|
||||
requires a .prev-version file as well as a Makefile, from which it
|
||||
extracts the version number and package name, respectively. Also, it
|
||||
assumes all documentation is in the doc/ sub-directory.
|
||||
|
||||
Options:
|
||||
-C, --builddir=DIR location of (configured) Makefile (default: .)
|
||||
-n, --dry-run don't actually commit anything
|
||||
-m, --mirror remove out of date files from document server
|
||||
--help print this help, then exit
|
||||
--version print version number, then exit
|
||||
|
||||
Report bugs and patches to <bug-gnulib@gnu.org>.
|
||||
EOF
|
||||
exit
|
||||
}
|
||||
|
||||
version()
|
||||
{
|
||||
year=$(echo "$VERSION" | sed 's/[^0-9].*//')
|
||||
cat <<EOF
|
||||
$ME $VERSION
|
||||
Copyright (C) $year Free Software Foundation, Inc,
|
||||
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law.
|
||||
EOF
|
||||
exit
|
||||
}
|
||||
|
||||
# find_tool ENVVAR NAMES...
|
||||
# -------------------------
|
||||
# Search for a required program. Use the value of ENVVAR, if set,
|
||||
# otherwise find the first of the NAMES that can be run (i.e.,
|
||||
# supports --version). If found, set ENVVAR to the program name,
|
||||
# die otherwise.
|
||||
#
|
||||
# FIXME: code duplication, see also bootstrap.
|
||||
find_tool ()
|
||||
{
|
||||
find_tool_envvar=$1
|
||||
shift
|
||||
find_tool_names=$@
|
||||
eval "find_tool_res=\$$find_tool_envvar"
|
||||
if test x"$find_tool_res" = x; then
|
||||
for i
|
||||
do
|
||||
if ($i --version </dev/null) >/dev/null 2>&1; then
|
||||
find_tool_res=$i
|
||||
break
|
||||
fi
|
||||
done
|
||||
else
|
||||
find_tool_error_prefix="\$$find_tool_envvar: "
|
||||
fi
|
||||
test x"$find_tool_res" != x \
|
||||
|| die "one of these is required: $find_tool_names"
|
||||
($find_tool_res --version </dev/null) >/dev/null 2>&1 \
|
||||
|| die "${find_tool_error_prefix}cannot run $find_tool_res --version"
|
||||
eval "$find_tool_envvar=\$find_tool_res"
|
||||
eval "export $find_tool_envvar"
|
||||
}
|
||||
|
||||
## ------ ##
|
||||
## Main. ##
|
||||
## ------ ##
|
||||
|
||||
# Requirements: everything required to bootstrap your package, plus
|
||||
# these.
|
||||
find_tool CVS cvs
|
||||
find_tool GIT git
|
||||
find_tool RSYNC rsync
|
||||
find_tool XARGS gxargs xargs
|
||||
|
||||
builddir=.
|
||||
dryrun=
|
||||
rm_stale='echo'
|
||||
while test $# != 0
|
||||
do
|
||||
# Handle --option=value by splitting apart and putting back on argv.
|
||||
case $1 in
|
||||
--*=*)
|
||||
opt=$(echo "$1" | sed -e 's/=.*//')
|
||||
val=$(echo "$1" | sed -e 's/[^=]*=//')
|
||||
shift
|
||||
set dummy "$opt" "$val" "$@"; shift
|
||||
;;
|
||||
esac
|
||||
|
||||
case $1 in
|
||||
--help|--version) ${1#--};;
|
||||
-C|--builddir) shift; builddir=$1; shift ;;
|
||||
-n|--dry-run) dryrun=echo; shift;;
|
||||
-m|--mirror) rm_stale=''; shift;;
|
||||
--*) die "unrecognized option: $1";;
|
||||
*) break;;
|
||||
esac
|
||||
done
|
||||
|
||||
test $# = 0 \
|
||||
|| die "too many arguments"
|
||||
|
||||
prev=.prev-version
|
||||
version=$(cat $prev) || die "no $prev file?"
|
||||
pkg=$(sed -n 's/^PACKAGE = \(.*\)/\1/p' $builddir/Makefile) \
|
||||
|| die "no Makefile?"
|
||||
tmp_branch=web-doc-$version-$$
|
||||
current_branch=$($GIT branch | sed -ne '/^\* /{s///;p;q;}')
|
||||
|
||||
cleanup()
|
||||
{
|
||||
__st=$?
|
||||
$dryrun rm -rf "$tmp"
|
||||
$GIT checkout "$current_branch"
|
||||
$GIT submodule update --recursive
|
||||
$GIT branch -d $tmp_branch
|
||||
exit $__st
|
||||
}
|
||||
trap cleanup 0
|
||||
trap 'exit $?' 1 2 13 15
|
||||
|
||||
# We must build using sources for which --version reports the
|
||||
# just-released version number, not some string like 7.6.18-20761.
|
||||
# That version string propagates into all documentation.
|
||||
set -e
|
||||
$GIT checkout -b $tmp_branch v$version
|
||||
$GIT submodule update --recursive
|
||||
./bootstrap
|
||||
srcdir=$(pwd)
|
||||
cd "$builddir"
|
||||
builddir=$(pwd)
|
||||
./config.status --recheck
|
||||
./config.status
|
||||
make
|
||||
make web-manual
|
||||
cd "$srcdir"
|
||||
set +e
|
||||
|
||||
tmp=$(mktemp -d web-doc-update.XXXXXX) || exit 1
|
||||
( cd $tmp \
|
||||
&& $CVS -d $USER@cvs.sv.gnu.org:/webcvs/$pkg co $pkg )
|
||||
$RSYNC -avP "$builddir"/doc/manual/ $tmp/$pkg/manual
|
||||
|
||||
(
|
||||
cd $tmp/$pkg/manual
|
||||
|
||||
# Add all the files. This is simpler than trying to add only the
|
||||
# new ones because of new directories
|
||||
# First add non empty dirs individually
|
||||
find . -name CVS -prune -o -type d \! -empty -print \
|
||||
| $XARGS -n1 --no-run-if-empty -- $dryrun $CVS add -ko
|
||||
# Now add all files
|
||||
find . -name CVS -prune -o -type f -print \
|
||||
| $XARGS --no-run-if-empty -- $dryrun $CVS add -ko
|
||||
|
||||
# Report/Remove stale files
|
||||
# excluding doc server specific files like CVS/* and .symlinks
|
||||
if test -n "$rm_stale"; then
|
||||
echo 'Consider the --mirror option if all of the manual is generated,' >&2
|
||||
echo 'which will run `cvs remove` to remove stale files.' >&2
|
||||
fi
|
||||
{ find . \( -name CVS -o -type f -name '.*' \) -prune -o -type f -print
|
||||
(cd "$builddir"/doc/manual/ && find . -type f -print | sed p)
|
||||
} | sort | uniq -u \
|
||||
| $XARGS --no-run-if-empty -- ${rm_stale:-$dryrun} $CVS remove -f
|
||||
|
||||
$dryrun $CVS ci -m $version
|
||||
)
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "VERSION="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
|
|
@ -1,440 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Sign files and upload them.
|
||||
|
||||
scriptversion=2018-03-07.03; # UTC
|
||||
|
||||
# Copyright (C) 2004-2018 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Originally written by Alexandre Duret-Lutz <adl@gnu.org>.
|
||||
# The master copy of this file is maintained in the gnulib Git repository.
|
||||
# Please send bug reports and feature requests to bug-gnulib@gnu.org.
|
||||
|
||||
set -e
|
||||
|
||||
GPG='gpg --batch --no-tty'
|
||||
conffile=.gnuploadrc
|
||||
to=
|
||||
dry_run=false
|
||||
replace=
|
||||
symlink_files=
|
||||
delete_files=
|
||||
delete_symlinks=
|
||||
collect_var=
|
||||
dbg=
|
||||
nl='
|
||||
'
|
||||
|
||||
usage="Usage: $0 [OPTION]... [CMD] FILE... [[CMD] FILE...]
|
||||
|
||||
Sign all FILES, and process them at the destinations specified with --to.
|
||||
If CMD is not given, it defaults to uploading. See examples below.
|
||||
|
||||
Commands:
|
||||
--delete delete FILES from destination
|
||||
--symlink create symbolic links
|
||||
--rmsymlink remove symbolic links
|
||||
-- treat the remaining arguments as files to upload
|
||||
|
||||
Options:
|
||||
--to DEST specify a destination DEST for FILES
|
||||
(multiple --to options are allowed)
|
||||
--user NAME sign with key NAME
|
||||
--replace allow replacements of existing files
|
||||
--symlink-regex[=EXPR] use sed script EXPR to compute symbolic link names
|
||||
--dry-run do nothing, show what would have been done
|
||||
(including the constructed directive file)
|
||||
--version output version information and exit
|
||||
--help print this help text and exit
|
||||
|
||||
If --symlink-regex is given without EXPR, then the link target name
|
||||
is created by replacing the version information with '-latest', e.g.:
|
||||
foo-1.3.4.tar.gz -> foo-latest.tar.gz
|
||||
|
||||
Recognized destinations are:
|
||||
alpha.gnu.org:DIRECTORY
|
||||
savannah.gnu.org:DIRECTORY
|
||||
savannah.nongnu.org:DIRECTORY
|
||||
ftp.gnu.org:DIRECTORY
|
||||
build directive files and upload files by FTP
|
||||
download.gnu.org.ua:{alpha|ftp}/DIRECTORY
|
||||
build directive files and upload files by SFTP
|
||||
[user@]host:DIRECTORY upload files with scp
|
||||
|
||||
Options and commands are applied in order. If the file $conffile exists
|
||||
in the current working directory, its contents are prepended to the
|
||||
actual command line options. Use this to keep your defaults. Comments
|
||||
(#) and empty lines in $conffile are allowed.
|
||||
|
||||
<https://www.gnu.org/prep/maintain/html_node/Automated-FTP-Uploads.html>
|
||||
gives some further background.
|
||||
|
||||
Examples:
|
||||
1. Upload foobar-1.0.tar.gz to ftp.gnu.org:
|
||||
gnupload --to ftp.gnu.org:foobar foobar-1.0.tar.gz
|
||||
|
||||
2. Upload foobar-1.0.tar.gz and foobar-1.0.tar.xz to ftp.gnu.org:
|
||||
gnupload --to ftp.gnu.org:foobar foobar-1.0.tar.gz foobar-1.0.tar.xz
|
||||
|
||||
3. Same as above, and also create symbolic links to foobar-latest.tar.*:
|
||||
gnupload --to ftp.gnu.org:foobar \\
|
||||
--symlink-regex \\
|
||||
foobar-1.0.tar.gz foobar-1.0.tar.xz
|
||||
|
||||
4. Upload foobar-0.9.90.tar.gz to two sites:
|
||||
gnupload --to alpha.gnu.org:foobar \\
|
||||
--to sources.redhat.com:~ftp/pub/foobar \\
|
||||
foobar-0.9.90.tar.gz
|
||||
|
||||
5. Delete oopsbar-0.9.91.tar.gz and upload foobar-0.9.91.tar.gz
|
||||
(the -- terminates the list of files to delete):
|
||||
gnupload --to alpha.gnu.org:foobar \\
|
||||
--to sources.redhat.com:~ftp/pub/foobar \\
|
||||
--delete oopsbar-0.9.91.tar.gz \\
|
||||
-- foobar-0.9.91.tar.gz
|
||||
|
||||
gnupload executes a program ncftpput to do the transfers; if you don't
|
||||
happen to have an ncftp package installed, the ncftpput-ftp script in
|
||||
the build-aux/ directory of the gnulib package
|
||||
(https://savannah.gnu.org/projects/gnulib) may serve as a replacement.
|
||||
|
||||
Send patches and bug reports to <bug-gnulib@gnu.org>."
|
||||
|
||||
# Read local configuration file
|
||||
if test -r "$conffile"; then
|
||||
echo "$0: Reading configuration file $conffile"
|
||||
conf=`sed 's/#.*$//;/^$/d' "$conffile" | tr "\015$nl" ' '`
|
||||
eval set x "$conf \"\$@\""
|
||||
shift
|
||||
fi
|
||||
|
||||
while test -n "$1"; do
|
||||
case $1 in
|
||||
-*)
|
||||
collect_var=
|
||||
case $1 in
|
||||
--help)
|
||||
echo "$usage"
|
||||
exit $?
|
||||
;;
|
||||
--to)
|
||||
if test -z "$2"; then
|
||||
echo "$0: Missing argument for --to" 1>&2
|
||||
exit 1
|
||||
elif echo "$2" | grep 'ftp-upload\.gnu\.org' >/dev/null; then
|
||||
echo "$0: Use ftp.gnu.org:PKGNAME or alpha.gnu.org:PKGNAME" >&2
|
||||
echo "$0: for the destination, not ftp-upload.gnu.org (which" >&2
|
||||
echo "$0: is used for direct ftp uploads, not with gnupload)." >&2
|
||||
echo "$0: See --help and its examples if need be." >&2
|
||||
exit 1
|
||||
else
|
||||
to="$to $2"
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
--user)
|
||||
if test -z "$2"; then
|
||||
echo "$0: Missing argument for --user" 1>&2
|
||||
exit 1
|
||||
else
|
||||
GPG="$GPG --local-user $2"
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
--delete)
|
||||
collect_var=delete_files
|
||||
;;
|
||||
--replace)
|
||||
replace="replace: true"
|
||||
;;
|
||||
--rmsymlink)
|
||||
collect_var=delete_symlinks
|
||||
;;
|
||||
--symlink-regex=*)
|
||||
symlink_expr=`expr "$1" : '[^=]*=\(.*\)'`
|
||||
;;
|
||||
--symlink-regex)
|
||||
symlink_expr='s|-[0-9][0-9\.]*\(-[0-9][0-9]*\)\{0,1\}\.|-latest.|'
|
||||
;;
|
||||
--symlink)
|
||||
collect_var=symlink_files
|
||||
;;
|
||||
--dry-run|-n)
|
||||
dry_run=:
|
||||
;;
|
||||
--version)
|
||||
echo "gnupload $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
echo "$0: Unknown option '$1', try '$0 --help'" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
if test -z "$collect_var"; then
|
||||
break
|
||||
else
|
||||
eval "$collect_var=\"\$$collect_var $1\""
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
dprint()
|
||||
{
|
||||
echo "Running $* ..."
|
||||
}
|
||||
|
||||
if $dry_run; then
|
||||
dbg=dprint
|
||||
fi
|
||||
|
||||
if test -z "$to"; then
|
||||
echo "$0: Missing destination sites" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -n "$symlink_files"; then
|
||||
x=`echo "$symlink_files" | sed 's/[^ ]//g;s/ //g'`
|
||||
if test -n "$x"; then
|
||||
echo "$0: Odd number of symlink arguments" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $# = 0; then
|
||||
if test -z "${symlink_files}${delete_files}${delete_symlinks}"; then
|
||||
echo "$0: No file to upload" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Make sure all files exist. We don't want to ask
|
||||
# for the passphrase if the script will fail.
|
||||
for file
|
||||
do
|
||||
if test ! -f $file; then
|
||||
echo "$0: Cannot find '$file'" 1>&2
|
||||
exit 1
|
||||
elif test -n "$symlink_expr"; then
|
||||
linkname=`echo $file | sed "$symlink_expr"`
|
||||
if test -z "$linkname"; then
|
||||
echo "$0: symlink expression produces empty results" >&2
|
||||
exit 1
|
||||
elif test "$linkname" = $file; then
|
||||
echo "$0: symlink expression does not alter file name" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Make sure passphrase is not exported in the environment.
|
||||
unset passphrase
|
||||
unset passphrase_fd_0
|
||||
GNUPGHOME=${GNUPGHOME:-$HOME/.gnupg}
|
||||
|
||||
# Reset PATH to be sure that echo is a built-in. We will later use
|
||||
# 'echo $passphrase' to output the passphrase, so it is important that
|
||||
# it is a built-in (third-party programs tend to appear in 'ps'
|
||||
# listings with their arguments...).
|
||||
# Remember this script runs with 'set -e', so if echo is not built-in
|
||||
# it will exit now.
|
||||
if $dry_run || grep -q "^use-agent" $GNUPGHOME/gpg.conf; then :; else
|
||||
PATH=/empty echo -n "Enter GPG passphrase: "
|
||||
stty -echo
|
||||
read -r passphrase
|
||||
stty echo
|
||||
echo
|
||||
passphrase_fd_0="--passphrase-fd 0"
|
||||
fi
|
||||
|
||||
if test $# -ne 0; then
|
||||
for file
|
||||
do
|
||||
echo "Signing $file ..."
|
||||
rm -f $file.sig
|
||||
echo "$passphrase" | $dbg $GPG $passphrase_fd_0 -ba -o $file.sig $file
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# mkdirective DESTDIR BASE FILE STMT
|
||||
# Arguments: See upload, below
|
||||
mkdirective ()
|
||||
{
|
||||
stmt="$4"
|
||||
if test -n "$3"; then
|
||||
stmt="
|
||||
filename: $3$stmt"
|
||||
fi
|
||||
|
||||
cat >${2}.directive<<EOF
|
||||
version: 1.2
|
||||
directory: $1
|
||||
comment: gnupload v. $scriptversion$stmt
|
||||
EOF
|
||||
if $dry_run; then
|
||||
echo "File ${2}.directive:"
|
||||
cat ${2}.directive
|
||||
echo "File ${2}.directive:" | sed 's/./-/g'
|
||||
fi
|
||||
}
|
||||
|
||||
mksymlink ()
|
||||
{
|
||||
while test $# -ne 0
|
||||
do
|
||||
echo "symlink: $1 $2"
|
||||
shift
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
# upload DEST DESTDIR BASE FILE STMT FILES
|
||||
# Arguments:
|
||||
# DEST Destination site;
|
||||
# DESTDIR Destination directory;
|
||||
# BASE Base name for the directive file;
|
||||
# FILE Name of the file to distribute (may be empty);
|
||||
# STMT Additional statements for the directive file;
|
||||
# FILES List of files to upload.
|
||||
upload ()
|
||||
{
|
||||
dest=$1
|
||||
destdir=$2
|
||||
base=$3
|
||||
file=$4
|
||||
stmt=$5
|
||||
files=$6
|
||||
|
||||
rm -f $base.directive $base.directive.asc
|
||||
case $dest in
|
||||
alpha.gnu.org:*)
|
||||
mkdirective "$destdir" "$base" "$file" "$stmt"
|
||||
echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
|
||||
$dbg ncftpput ftp-upload.gnu.org /incoming/alpha $files $base.directive.asc
|
||||
;;
|
||||
ftp.gnu.org:*)
|
||||
mkdirective "$destdir" "$base" "$file" "$stmt"
|
||||
echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
|
||||
$dbg ncftpput ftp-upload.gnu.org /incoming/ftp $files $base.directive.asc
|
||||
;;
|
||||
savannah.gnu.org:*)
|
||||
if test -z "$files"; then
|
||||
echo "$0: warning: standalone directives not applicable for $dest" >&2
|
||||
fi
|
||||
$dbg ncftpput savannah.gnu.org /incoming/savannah/$destdir $files
|
||||
;;
|
||||
savannah.nongnu.org:*)
|
||||
if test -z "$files"; then
|
||||
echo "$0: warning: standalone directives not applicable for $dest" >&2
|
||||
fi
|
||||
$dbg ncftpput savannah.nongnu.org /incoming/savannah/$destdir $files
|
||||
;;
|
||||
download.gnu.org.ua:alpha/*|download.gnu.org.ua:ftp/*)
|
||||
destdir_p1=`echo "$destdir" | sed 's,^[^/]*/,,'`
|
||||
destdir_topdir=`echo "$destdir" | sed 's,/.*,,'`
|
||||
mkdirective "$destdir_p1" "$base" "$file" "$stmt"
|
||||
echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
|
||||
for f in $files $base.directive.asc
|
||||
do
|
||||
echo put $f
|
||||
done | $dbg sftp -b - puszcza.gnu.org.ua:/incoming/$destdir_topdir
|
||||
;;
|
||||
/*)
|
||||
dest_host=`echo "$dest" | sed 's,:.*,,'`
|
||||
mkdirective "$destdir" "$base" "$file" "$stmt"
|
||||
echo "$passphrase" | $dbg $GPG $passphrase_fd_0 --clearsign $base.directive
|
||||
$dbg cp $files $base.directive.asc $dest_host
|
||||
;;
|
||||
*)
|
||||
if test -z "$files"; then
|
||||
echo "$0: warning: standalone directives not applicable for $dest" >&2
|
||||
fi
|
||||
$dbg scp $files $dest
|
||||
;;
|
||||
esac
|
||||
rm -f $base.directive $base.directive.asc
|
||||
}
|
||||
|
||||
#####
|
||||
# Process any standalone directives
|
||||
stmt=
|
||||
if test -n "$symlink_files"; then
|
||||
stmt="$stmt
|
||||
`mksymlink $symlink_files`"
|
||||
fi
|
||||
|
||||
for file in $delete_files
|
||||
do
|
||||
stmt="$stmt
|
||||
archive: $file"
|
||||
done
|
||||
|
||||
for file in $delete_symlinks
|
||||
do
|
||||
stmt="$stmt
|
||||
rmsymlink: $file"
|
||||
done
|
||||
|
||||
if test -n "$stmt"; then
|
||||
for dest in $to
|
||||
do
|
||||
destdir=`echo $dest | sed 's/[^:]*://'`
|
||||
upload "$dest" "$destdir" "`hostname`-$$" "" "$stmt"
|
||||
done
|
||||
fi
|
||||
|
||||
# Process actual uploads
|
||||
for dest in $to
|
||||
do
|
||||
for file
|
||||
do
|
||||
echo "Uploading $file to $dest ..."
|
||||
stmt=
|
||||
#
|
||||
# allowing file replacement is all or nothing.
|
||||
if test -n "$replace"; then stmt="$stmt
|
||||
$replace"
|
||||
fi
|
||||
#
|
||||
files="$file $file.sig"
|
||||
destdir=`echo $dest | sed 's/[^:]*://'`
|
||||
if test -n "$symlink_expr"; then
|
||||
linkname=`echo $file | sed "$symlink_expr"`
|
||||
stmt="$stmt
|
||||
symlink: $file $linkname
|
||||
symlink: $file.sig $linkname.sig"
|
||||
fi
|
||||
upload "$dest" "$destdir" "$file" "$file" "$stmt" "$files"
|
||||
done
|
||||
done
|
||||
|
||||
exit 0
|
||||
|
||||
# Local variables:
|
||||
# eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC0"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
;;;; guix.scm -- Guix package definition
|
||||
;;; Copyright © 2016 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;; Copyright © 2018 宋文武 <iyzsong@member.fsf.org>
|
||||
;;; Copyright © 2019 Efraim Flashner <efraim@flashner.co.il>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(use-modules (ice-9 popen)
|
||||
(ice-9 rdelim)
|
||||
(gnu)
|
||||
(guix)
|
||||
(srfi srfi-1))
|
||||
|
||||
(define (keep-mcron-file? file stat)
|
||||
;; Return #t if FILE in Mcron repository must be kept, #f otherwise. FILE
|
||||
;; is an absolute file name and STAT is the result of 'lstat' applied to
|
||||
;; FILE.
|
||||
(not (or (any (λ (str) (string-contains file str))
|
||||
'(".git" "autom4te" "Makefile.in" ".go" ".log"
|
||||
"stamp-vti" ".dirstamp"))
|
||||
(any (λ (str) (string-suffix? str file))
|
||||
'("trs" "configure" "Makefile" "config.status" "pre-inst-env"
|
||||
"aclocal.m4" "bin/cron" "bin/mcron" "bin/crontab"
|
||||
"config.cache" "guix.scm")))))
|
||||
|
||||
(define %srcdir
|
||||
(or (current-source-directory) "."))
|
||||
|
||||
(package
|
||||
(inherit (specification->package "mcron"))
|
||||
(version "1.2.0")
|
||||
(source (local-file (dirname %srcdir) #:recursive? #t
|
||||
#:select? keep-mcron-file?))
|
||||
(inputs
|
||||
`(("guile" ,(specification->package "guile@2.2"))))
|
||||
(native-inputs
|
||||
`(("autoconf" ,(specification->package "autoconf"))
|
||||
("automake" ,(specification->package "automake"))
|
||||
("help2man" ,(specification->package "help2man"))
|
||||
("pkg-config" ,(specification->package "pkg-config"))
|
||||
("texinfo" ,(specification->package "texinfo"))
|
||||
("tzdata" ,(specification->package "tzdata")))))
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
|
||||
#
|
||||
# This file is part of GNU Mcron.
|
||||
#
|
||||
# GNU Mcron is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# GNU Mcron is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
abs_top_srcdir="`cd "@abs_top_srcdir@" > /dev/null; pwd`"
|
||||
abs_top_builddir="`cd "@abs_top_builddir@" > /dev/null; pwd`"
|
||||
|
||||
GUILE_LOAD_COMPILED_PATH="$abs_top_builddir/src${GUILE_LOAD_COMPILED_PATH:+:}$GUILE_LOAD_COMPILED_PATH"
|
||||
GUILE_LOAD_PATH="$abs_top_builddir/src:$abs_top_srcdir/src${GUILE_LOAD_PATH:+:}:$GUILE_LOAD_PATH"
|
||||
export GUILE_LOAD_COMPILED_PATH GUILE_LOAD_PATH
|
||||
|
||||
PATH="$abs_top_builddir/bin:$PATH"
|
||||
export PATH
|
||||
|
||||
# Define $MCRON_UNINSTALLED to prevent 'mcron' from prepending @moduledir@ to
|
||||
# the Guile load paths.
|
||||
MCRON_UNINSTALLED=1
|
||||
export MCRON_UNINSTALLED
|
||||
|
||||
srcdir="@srcdir@"
|
||||
export srcdir
|
||||
|
||||
exec "$@"
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
;;;; test-driver.scm - Guile test driver for Automake testsuite harness
|
||||
|
||||
(define script-version "2018-03-25.05") ;UTC
|
||||
|
||||
;;; Copyright © 2015-2018 Free Software Foundation, Inc.
|
||||
;;;
|
||||
;;; This program is free software; you can redistribute it and/or modify it
|
||||
;;; under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation; either version 3 of the License, or (at
|
||||
;;; your option) any later version.
|
||||
;;;
|
||||
;;; This program is distributed in the hope that it will be useful, but
|
||||
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;;; Commentary:
|
||||
;;;
|
||||
;;; This script provides a Guile test driver using the SRFI-64 Scheme API for
|
||||
;;; test suites. SRFI-64 is distributed with Guile since version 2.0.9.
|
||||
;;;
|
||||
;;; To use it, you have to manually copy this file in the ‘build-aux’
|
||||
;;; directory of your package, then adapt the following snippets to your
|
||||
;;; actual needs:
|
||||
;;;
|
||||
;;; configure.ac:
|
||||
;;; AC_CONFIG_AUX_DIR([build-aux])
|
||||
;;; AC_REQUIRE_AUX_FILE([test-driver.scm])
|
||||
;;; AC_PATH_PROG([GUILE], [guile])
|
||||
;;;
|
||||
;;; Makefile.am
|
||||
;;; TEST_LOG_DRIVER = $(GUILE) $(top_srcdir)/build-aux/test-driver.scm
|
||||
;;; AM_TESTS_ENVIRONMENT = env GUILE_AUTO_COMPILE='0'
|
||||
;;; TESTS = foo.test
|
||||
;;; EXTRA_DIST = $(TESTS)
|
||||
;;;
|
||||
;;; foo.test
|
||||
;;; (use-modules (srfi srfi-64))
|
||||
;;; (test-begin "foo")
|
||||
;;; (test-assert "assertion example" #t)
|
||||
;;; (test-end "foo")
|
||||
;;;
|
||||
;;; See <https://srfi.schemers.org/srfi-64/srfi-64.html> for general
|
||||
;;; information about SRFI-64 usage.
|
||||
;;;
|
||||
;;;; Code:
|
||||
|
||||
(use-modules (ice-9 getopt-long)
|
||||
(ice-9 match)
|
||||
(ice-9 pretty-print)
|
||||
(srfi srfi-11)
|
||||
(srfi srfi-26)
|
||||
(srfi srfi-64)
|
||||
(system vm coverage)
|
||||
(system vm vm))
|
||||
|
||||
(define (show-help)
|
||||
(display "Usage:
|
||||
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||
[--enable-hard-errors={yes|no}] [--brief={yes|no}}]
|
||||
[--coverage={yes|no}] [--]
|
||||
TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
|
||||
The '--test-name', '--log-file' and '--trs-file' options are mandatory.\n"))
|
||||
|
||||
(define %options
|
||||
'((test-name (value #t))
|
||||
(log-file (value #t))
|
||||
(trs-file (value #t))
|
||||
(color-tests (value #t))
|
||||
(expect-failure (value #t)) ;XXX: not implemented yet
|
||||
(enable-hard-errors (value #t)) ;not implemented in SRFI-64
|
||||
(coverage (value #t))
|
||||
(brief (value #t))
|
||||
(help (single-char #\h) (value #f))
|
||||
(version (single-char #\V) (value #f))))
|
||||
|
||||
(define (option->boolean options key)
|
||||
"Return #t if the value associated with KEY in OPTIONS is \"yes\"."
|
||||
(and=> (option-ref options key #f) (cut string=? <> "yes")))
|
||||
|
||||
(define* (test-display field value #:optional (port (current-output-port))
|
||||
#:key pretty?)
|
||||
"Display \"FIELD: VALUE\\n\" on PORT."
|
||||
(if pretty?
|
||||
(begin
|
||||
(format port "~A:~%" field)
|
||||
(pretty-print value port #:per-line-prefix "+ "))
|
||||
(format port "~A: ~S~%" field value)))
|
||||
|
||||
(define* (result->string symbol #:key colorize?)
|
||||
"Return SYMBOL as an upper case string. Use colors when COLORIZE is #t."
|
||||
(let ((result (string-upcase (symbol->string symbol))))
|
||||
(if colorize?
|
||||
(string-append (case symbol
|
||||
((pass) "[0;32m") ;green
|
||||
((xfail) "[1;32m") ;light green
|
||||
((skip) "[1;34m") ;blue
|
||||
((fail xpass) "[0;31m") ;red
|
||||
((error) "[0;35m")) ;magenta
|
||||
result
|
||||
"[m") ;no color
|
||||
result)))
|
||||
|
||||
(define* (test-runner-gnu test-name #:key color? brief? out-port trs-port)
|
||||
"Return an custom SRFI-64 test runner. TEST-NAME is a string specifying the
|
||||
file name of the current the test. COLOR? specifies whether to use colors,
|
||||
and BRIEF?, well, you know. OUT-PORT and TRS-PORT must be output ports. The
|
||||
current output port is supposed to be redirected to a '.log' file."
|
||||
|
||||
(define (test-on-test-begin-gnu runner)
|
||||
;; Procedure called at the start of an individual test case, before the
|
||||
;; test expression (and expected value) are evaluated.
|
||||
(let ((result (cute assq-ref (test-result-alist runner) <>)))
|
||||
(format #t "test-name: ~A~%" (result 'test-name))
|
||||
(format #t "location: ~A~%"
|
||||
(string-append (result 'source-file) ":"
|
||||
(number->string (result 'source-line))))
|
||||
(test-display "source" (result 'source-form) #:pretty? #t)))
|
||||
|
||||
(define (test-on-test-end-gnu runner)
|
||||
;; Procedure called at the end of an individual test case, when the result
|
||||
;; of the test is available.
|
||||
(let* ((results (test-result-alist runner))
|
||||
(result? (cut assq <> results))
|
||||
(result (cut assq-ref results <>)))
|
||||
(unless brief?
|
||||
;; Display the result of each test case on the console.
|
||||
(format out-port "~A: ~A - ~A~%"
|
||||
(result->string (test-result-kind runner) #:colorize? color?)
|
||||
test-name (test-runner-test-name runner)))
|
||||
(when (result? 'expected-value)
|
||||
(test-display "expected-value" (result 'expected-value)))
|
||||
(when (result? 'expected-error)
|
||||
(test-display "expected-error" (result 'expected-error) #:pretty? #t))
|
||||
(when (result? 'actual-value)
|
||||
(test-display "actual-value" (result 'actual-value)))
|
||||
(when (result? 'actual-error)
|
||||
(test-display "actual-error" (result 'actual-error) #:pretty? #t))
|
||||
(format #t "result: ~a~%" (result->string (result 'result-kind)))
|
||||
(newline)
|
||||
(format trs-port ":test-result: ~A ~A~%"
|
||||
(result->string (test-result-kind runner))
|
||||
(test-runner-test-name runner))))
|
||||
|
||||
(define (test-on-group-end-gnu runner)
|
||||
;; Procedure called by a 'test-end', including at the end of a test-group.
|
||||
(let ((fail (or (positive? (test-runner-fail-count runner))
|
||||
(positive? (test-runner-xpass-count runner))))
|
||||
(skip (or (positive? (test-runner-skip-count runner))
|
||||
(positive? (test-runner-xfail-count runner)))))
|
||||
;; XXX: The global results need some refinements for XPASS.
|
||||
(format trs-port ":global-test-result: ~A~%"
|
||||
(if fail "FAIL" (if skip "SKIP" "PASS")))
|
||||
(format trs-port ":recheck: ~A~%"
|
||||
(if fail "yes" "no"))
|
||||
(format trs-port ":copy-in-global-log: ~A~%"
|
||||
(if (or fail skip) "yes" "no"))
|
||||
(when brief?
|
||||
;; Display the global test group result on the console.
|
||||
(format out-port "~A: ~A~%"
|
||||
(result->string (if fail 'fail (if skip 'skip 'pass))
|
||||
#:colorize? color?)
|
||||
test-name))
|
||||
#f))
|
||||
|
||||
(let ((runner (test-runner-null)))
|
||||
(test-runner-on-test-begin! runner test-on-test-begin-gnu)
|
||||
(test-runner-on-test-end! runner test-on-test-end-gnu)
|
||||
(test-runner-on-group-end! runner test-on-group-end-gnu)
|
||||
(test-runner-on-bad-end-name! runner test-on-bad-end-name-simple)
|
||||
runner))
|
||||
|
||||
|
||||
;;;
|
||||
;;; Entry point.
|
||||
;;;
|
||||
|
||||
(let* ((opts (getopt-long (command-line) %options))
|
||||
(option (cut option-ref opts <> <>)))
|
||||
(cond
|
||||
((option 'help #f) (show-help))
|
||||
((option 'version #f) (format #t "test-driver.scm ~A" script-version))
|
||||
(else
|
||||
(match (option '() '())
|
||||
(()
|
||||
(display "missing test script argument\n" (current-error-port))
|
||||
(exit 1))
|
||||
((script . args)
|
||||
(let ((log (open-file (option 'log-file "") "w0"))
|
||||
(trs (open-file (option 'trs-file "") "wl"))
|
||||
(out (duplicate-port (current-output-port) "wl")))
|
||||
(define (check)
|
||||
(test-with-runner
|
||||
(test-runner-gnu (option 'test-name #f)
|
||||
#:color? (option->boolean opts 'color-tests)
|
||||
#:brief? (option->boolean opts 'brief)
|
||||
#:out-port out #:trs-port trs)
|
||||
(primitive-load script)))
|
||||
|
||||
(redirect-port log (current-output-port))
|
||||
(redirect-port log (current-warning-port))
|
||||
(redirect-port log (current-error-port))
|
||||
|
||||
(if (not (option->boolean opts 'coverage))
|
||||
(check)
|
||||
(begin
|
||||
;; The debug engine is required for tracing coverage data.
|
||||
(set-vm-engine! 'debug)
|
||||
(let-values (((data result) (with-code-coverage check)))
|
||||
(let* ((file (string-append (option 'test-name #f) ".info"))
|
||||
(port (open-output-file file)))
|
||||
(coverage-data->lcov data port)
|
||||
(close port)))))
|
||||
|
||||
(close-port log)
|
||||
(close-port trs)
|
||||
(close-port out))))))
|
||||
(exit 0))
|
||||
|
||||
;;; Local Variables:
|
||||
;;; eval: (add-hook 'before-save-hook 'time-stamp)
|
||||
;;; time-stamp-start: "(define script-version \""
|
||||
;;; time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
;;; time-stamp-time-zone: "UTC0"
|
||||
;;; time-stamp-end: "\") ;UTC"
|
||||
;;; End:
|
||||
|
||||
;;;; test-driver.scm ends here.
|
||||
135
configure.ac
135
configure.ac
|
|
@ -1,135 +0,0 @@
|
|||
## Process this file with autoconf to produce a configure script.
|
||||
#
|
||||
# Copyright © 2003, 2005, 2012, 2014 Dale Mellor <mcron-lsfnyl@rdmp.org>
|
||||
# Copyright © 2015, 2016, 2017, 2018, 2020 Mathieu Lirzin <mthl@gnu.org>
|
||||
# Copyright © 2018 宋文武 <iyzsong@member.fsf.org>
|
||||
#
|
||||
# This file is part of GNU Mcron.
|
||||
#
|
||||
# GNU Mcron is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# GNU Mcron is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT([GNU Mcron], [1.2.0+dmbcs], [bug-mcron@gnu.org])
|
||||
AC_CONFIG_SRCDIR([src/mcron/scripts/mcron.scm])
|
||||
AC_CONFIG_AUX_DIR([build-aux])
|
||||
AC_REQUIRE_AUX_FILE([test-driver.scm])
|
||||
|
||||
dnl We're fine with GNU make constructs, hence '-Wno-portability'.
|
||||
AM_INIT_AUTOMAKE([1.11 gnu silent-rules subdir-objects color-tests
|
||||
-Wall -Wno-override -Wno-portability std-options])
|
||||
|
||||
AM_SILENT_RULES([yes]) # Enables silent rules by default.
|
||||
|
||||
AC_CANONICAL_HOST
|
||||
|
||||
dnl We require pkg.m4 (from pkg-config) and guile.m4 (from Guile.)
|
||||
dnl Make sure they are available when generating the configure script.
|
||||
m4_pattern_forbid([^PKG_PROG])
|
||||
m4_pattern_forbid([^PKG_CHECK])
|
||||
m4_pattern_forbid([^GUILE_P])
|
||||
m4_pattern_allow([^GUILE_PKG_ERRORS])
|
||||
|
||||
# Check for Guile development files.
|
||||
GUILE_PKG([3.0 2.2 2.0])
|
||||
|
||||
# Checks for programs.
|
||||
GUILE_PROGS
|
||||
|
||||
AM_MISSING_PROG(HELP2MAN, help2man, $missing_dir)
|
||||
|
||||
# Let users choose the Mail Transfert Agent (MTA) of their choice. Default to
|
||||
# a non-absolute program name to make it a loose dependency resolved at
|
||||
# runtime.
|
||||
AC_ARG_WITH([sendmail],
|
||||
[AS_HELP_STRING([--with-sendmail=COMMAND],
|
||||
[command to read an email message from standard input, and send it])],
|
||||
[SENDMAIL="$withval"],
|
||||
[SENDMAIL="sendmail -t"])
|
||||
AC_SUBST([SENDMAIL])
|
||||
|
||||
AC_ARG_ENABLE([multi-user],
|
||||
[AS_HELP_STRING([--disable-multi-user],
|
||||
[Don't Install legacy cron and crontab programs])],
|
||||
[enable_multi_user="$enableval"],
|
||||
[enable_multi_user="yes"])
|
||||
AM_CONDITIONAL([MULTI_USER], [test "x$enable_multi_user" = xyes])
|
||||
|
||||
# Configure the various files that mcron uses at runtime.
|
||||
|
||||
AC_MSG_CHECKING([which spool directory to use])
|
||||
AC_ARG_WITH(spool-dir,
|
||||
AC_HELP_STRING([--with-spool-dir],
|
||||
[the crontab spool directory (/var/cron/tabs)]),
|
||||
CONFIG_SPOOL_DIR=$withval,
|
||||
CONFIG_SPOOL_DIR=[/var/cron/tabs])
|
||||
AC_MSG_RESULT($CONFIG_SPOOL_DIR)
|
||||
AC_SUBST(CONFIG_SPOOL_DIR)
|
||||
|
||||
AC_MSG_CHECKING([name of socket])
|
||||
AC_ARG_WITH(socket-file,
|
||||
AC_HELP_STRING([--with-socket-file],
|
||||
[unix pathname for cron socket (/var/cron/socket)]),
|
||||
CONFIG_SOCKET_FILE=$withval,
|
||||
CONFIG_SOCKET_FILE=[/var/cron/socket])
|
||||
AC_MSG_RESULT($CONFIG_SOCKET_FILE)
|
||||
AC_SUBST(CONFIG_SOCKET_FILE)
|
||||
|
||||
AC_MSG_CHECKING([name of allow file])
|
||||
AC_ARG_WITH(allow-file,
|
||||
AC_HELP_STRING([--with-allow-file],
|
||||
[the file of allowed users (/var/cron/allow)]),
|
||||
CONFIG_ALLOW_FILE=$withval,
|
||||
CONFIG_ALLOW_FILE=[/var/cron/allow])
|
||||
AC_MSG_RESULT($CONFIG_ALLOW_FILE)
|
||||
AC_SUBST(CONFIG_ALLOW_FILE)
|
||||
|
||||
AC_MSG_CHECKING([name of deny file])
|
||||
AC_ARG_WITH(deny-file,
|
||||
AC_HELP_STRING([--with-deny-file],
|
||||
[the file of barred users (/var/cron/deny)]),
|
||||
CONFIG_DENY_FILE=$withval,
|
||||
CONFIG_DENY_FILE=[/var/cron/deny])
|
||||
AC_MSG_RESULT($CONFIG_DENY_FILE)
|
||||
AC_SUBST(CONFIG_DENY_FILE)
|
||||
|
||||
AC_MSG_CHECKING([name of PID file])
|
||||
AC_ARG_WITH(pid-file,
|
||||
AC_HELP_STRING([--with-pid-file],
|
||||
[the file to record cron's PID (/var/run/cron.pid)]),
|
||||
CONFIG_PID_FILE=$withval,
|
||||
CONFIG_PID_FILE=[/var/run/cron.pid])
|
||||
AC_MSG_RESULT($CONFIG_PID_FILE)
|
||||
AC_SUBST(CONFIG_PID_FILE)
|
||||
|
||||
AC_MSG_CHECKING([directory to hold temporary files])
|
||||
AC_ARG_WITH(tmp-dir,
|
||||
AC_HELP_STRING([--with-tmp-dir],
|
||||
[directory to hold temporary files (/tmp)]),
|
||||
CONFIG_TMP_DIR=$withval,
|
||||
CONFIG_TMP_DIR=[/tmp])
|
||||
AC_MSG_RESULT($CONFIG_TMP_DIR)
|
||||
AC_SUBST(CONFIG_TMP_DIR)
|
||||
|
||||
# Include the Maintainer's Makefile fragment, if it's here.
|
||||
MAINT_MAKEFILE=/dev/null
|
||||
AS_IF([test -r "$srcdir/maint.mk"],
|
||||
[MAINT_MAKEFILE="$srcdir/maint.mk"])
|
||||
AC_SUBST_FILE([MAINT_MAKEFILE])
|
||||
|
||||
AC_CONFIG_FILES([pre-inst-env:build-aux/pre-inst-env.in],
|
||||
[chmod +x pre-inst-env])
|
||||
AC_CONFIG_FILES([doc/config.texi
|
||||
Makefile
|
||||
src/mcron/config.scm])
|
||||
AC_OUTPUT
|
||||
BIN
dale.key
Normal file
BIN
dale.key
Normal file
Binary file not shown.
|
|
@ -1,5 +0,0 @@
|
|||
@set CONFIG_SOCKET_FILE @CONFIG_SOCKET_FILE@
|
||||
@set CONFIG_SPOOL_DIR @CONFIG_SPOOL_DIR@
|
||||
@set CONFIG_PID_FILE @CONFIG_PID_FILE@
|
||||
@set CONFIG_ALLOW_FILE @CONFIG_ALLOW_FILE@
|
||||
@set CONFIG_DENY_FILE @CONFIG_DENY_FILE@
|
||||
505
doc/fdl.texi
505
doc/fdl.texi
|
|
@ -1,505 +0,0 @@
|
|||
@c The GNU Free Documentation License.
|
||||
@center Version 1.3, 3 November 2008
|
||||
|
||||
@c This file is intended to be included within another document,
|
||||
@c hence no sectioning command or @node.
|
||||
|
||||
@display
|
||||
Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
|
||||
@uref{http://fsf.org/}
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
@end display
|
||||
|
||||
@enumerate 0
|
||||
@item
|
||||
PREAMBLE
|
||||
|
||||
The purpose of this License is to make a manual, textbook, or other
|
||||
functional and useful document @dfn{free} in the sense of freedom: to
|
||||
assure everyone the effective freedom to copy and redistribute it,
|
||||
with or without modifying it, either commercially or noncommercially.
|
||||
Secondarily, this License preserves for the author and publisher a way
|
||||
to get credit for their work, while not being considered responsible
|
||||
for modifications made by others.
|
||||
|
||||
This License is a kind of ``copyleft'', which means that derivative
|
||||
works of the document must themselves be free in the same sense. It
|
||||
complements the GNU General Public License, which is a copyleft
|
||||
license designed for free software.
|
||||
|
||||
We have designed this License in order to use it for manuals for free
|
||||
software, because free software needs free documentation: a free
|
||||
program should come with manuals providing the same freedoms that the
|
||||
software does. But this License is not limited to software manuals;
|
||||
it can be used for any textual work, regardless of subject matter or
|
||||
whether it is published as a printed book. We recommend this License
|
||||
principally for works whose purpose is instruction or reference.
|
||||
|
||||
@item
|
||||
APPLICABILITY AND DEFINITIONS
|
||||
|
||||
This License applies to any manual or other work, in any medium, that
|
||||
contains a notice placed by the copyright holder saying it can be
|
||||
distributed under the terms of this License. Such a notice grants a
|
||||
world-wide, royalty-free license, unlimited in duration, to use that
|
||||
work under the conditions stated herein. The ``Document'', below,
|
||||
refers to any such manual or work. Any member of the public is a
|
||||
licensee, and is addressed as ``you''. You accept the license if you
|
||||
copy, modify or distribute the work in a way requiring permission
|
||||
under copyright law.
|
||||
|
||||
A ``Modified Version'' of the Document means any work containing the
|
||||
Document or a portion of it, either copied verbatim, or with
|
||||
modifications and/or translated into another language.
|
||||
|
||||
A ``Secondary Section'' is a named appendix or a front-matter section
|
||||
of the Document that deals exclusively with the relationship of the
|
||||
publishers or authors of the Document to the Document's overall
|
||||
subject (or to related matters) and contains nothing that could fall
|
||||
directly within that overall subject. (Thus, if the Document is in
|
||||
part a textbook of mathematics, a Secondary Section may not explain
|
||||
any mathematics.) The relationship could be a matter of historical
|
||||
connection with the subject or with related matters, or of legal,
|
||||
commercial, philosophical, ethical or political position regarding
|
||||
them.
|
||||
|
||||
The ``Invariant Sections'' are certain Secondary Sections whose titles
|
||||
are designated, as being those of Invariant Sections, in the notice
|
||||
that says that the Document is released under this License. If a
|
||||
section does not fit the above definition of Secondary then it is not
|
||||
allowed to be designated as Invariant. The Document may contain zero
|
||||
Invariant Sections. If the Document does not identify any Invariant
|
||||
Sections then there are none.
|
||||
|
||||
The ``Cover Texts'' are certain short passages of text that are listed,
|
||||
as Front-Cover Texts or Back-Cover Texts, in the notice that says that
|
||||
the Document is released under this License. A Front-Cover Text may
|
||||
be at most 5 words, and a Back-Cover Text may be at most 25 words.
|
||||
|
||||
A ``Transparent'' copy of the Document means a machine-readable copy,
|
||||
represented in a format whose specification is available to the
|
||||
general public, that is suitable for revising the document
|
||||
straightforwardly with generic text editors or (for images composed of
|
||||
pixels) generic paint programs or (for drawings) some widely available
|
||||
drawing editor, and that is suitable for input to text formatters or
|
||||
for automatic translation to a variety of formats suitable for input
|
||||
to text formatters. A copy made in an otherwise Transparent file
|
||||
format whose markup, or absence of markup, has been arranged to thwart
|
||||
or discourage subsequent modification by readers is not Transparent.
|
||||
An image format is not Transparent if used for any substantial amount
|
||||
of text. A copy that is not ``Transparent'' is called ``Opaque''.
|
||||
|
||||
Examples of suitable formats for Transparent copies include plain
|
||||
ASCII without markup, Texinfo input format, La@TeX{} input
|
||||
format, SGML or XML using a publicly available
|
||||
DTD, and standard-conforming simple HTML,
|
||||
PostScript or PDF designed for human modification. Examples
|
||||
of transparent image formats include PNG, XCF and
|
||||
JPG@. Opaque formats include proprietary formats that can be
|
||||
read and edited only by proprietary word processors, SGML or
|
||||
XML for which the DTD and/or processing tools are
|
||||
not generally available, and the machine-generated HTML,
|
||||
PostScript or PDF produced by some word processors for
|
||||
output purposes only.
|
||||
|
||||
The ``Title Page'' means, for a printed book, the title page itself,
|
||||
plus such following pages as are needed to hold, legibly, the material
|
||||
this License requires to appear in the title page. For works in
|
||||
formats which do not have any title page as such, ``Title Page'' means
|
||||
the text near the most prominent appearance of the work's title,
|
||||
preceding the beginning of the body of the text.
|
||||
|
||||
The ``publisher'' means any person or entity that distributes copies
|
||||
of the Document to the public.
|
||||
|
||||
A section ``Entitled XYZ'' means a named subunit of the Document whose
|
||||
title either is precisely XYZ or contains XYZ in parentheses following
|
||||
text that translates XYZ in another language. (Here XYZ stands for a
|
||||
specific section name mentioned below, such as ``Acknowledgements'',
|
||||
``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title''
|
||||
of such a section when you modify the Document means that it remains a
|
||||
section ``Entitled XYZ'' according to this definition.
|
||||
|
||||
The Document may include Warranty Disclaimers next to the notice which
|
||||
states that this License applies to the Document. These Warranty
|
||||
Disclaimers are considered to be included by reference in this
|
||||
License, but only as regards disclaiming warranties: any other
|
||||
implication that these Warranty Disclaimers may have is void and has
|
||||
no effect on the meaning of this License.
|
||||
|
||||
@item
|
||||
VERBATIM COPYING
|
||||
|
||||
You may copy and distribute the Document in any medium, either
|
||||
commercially or noncommercially, provided that this License, the
|
||||
copyright notices, and the license notice saying this License applies
|
||||
to the Document are reproduced in all copies, and that you add no other
|
||||
conditions whatsoever to those of this License. You may not use
|
||||
technical measures to obstruct or control the reading or further
|
||||
copying of the copies you make or distribute. However, you may accept
|
||||
compensation in exchange for copies. If you distribute a large enough
|
||||
number of copies you must also follow the conditions in section 3.
|
||||
|
||||
You may also lend copies, under the same conditions stated above, and
|
||||
you may publicly display copies.
|
||||
|
||||
@item
|
||||
COPYING IN QUANTITY
|
||||
|
||||
If you publish printed copies (or copies in media that commonly have
|
||||
printed covers) of the Document, numbering more than 100, and the
|
||||
Document's license notice requires Cover Texts, you must enclose the
|
||||
copies in covers that carry, clearly and legibly, all these Cover
|
||||
Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
|
||||
the back cover. Both covers must also clearly and legibly identify
|
||||
you as the publisher of these copies. The front cover must present
|
||||
the full title with all words of the title equally prominent and
|
||||
visible. You may add other material on the covers in addition.
|
||||
Copying with changes limited to the covers, as long as they preserve
|
||||
the title of the Document and satisfy these conditions, can be treated
|
||||
as verbatim copying in other respects.
|
||||
|
||||
If the required texts for either cover are too voluminous to fit
|
||||
legibly, you should put the first ones listed (as many as fit
|
||||
reasonably) on the actual cover, and continue the rest onto adjacent
|
||||
pages.
|
||||
|
||||
If you publish or distribute Opaque copies of the Document numbering
|
||||
more than 100, you must either include a machine-readable Transparent
|
||||
copy along with each Opaque copy, or state in or with each Opaque copy
|
||||
a computer-network location from which the general network-using
|
||||
public has access to download using public-standard network protocols
|
||||
a complete Transparent copy of the Document, free of added material.
|
||||
If you use the latter option, you must take reasonably prudent steps,
|
||||
when you begin distribution of Opaque copies in quantity, to ensure
|
||||
that this Transparent copy will remain thus accessible at the stated
|
||||
location until at least one year after the last time you distribute an
|
||||
Opaque copy (directly or through your agents or retailers) of that
|
||||
edition to the public.
|
||||
|
||||
It is requested, but not required, that you contact the authors of the
|
||||
Document well before redistributing any large number of copies, to give
|
||||
them a chance to provide you with an updated version of the Document.
|
||||
|
||||
@item
|
||||
MODIFICATIONS
|
||||
|
||||
You may copy and distribute a Modified Version of the Document under
|
||||
the conditions of sections 2 and 3 above, provided that you release
|
||||
the Modified Version under precisely this License, with the Modified
|
||||
Version filling the role of the Document, thus licensing distribution
|
||||
and modification of the Modified Version to whoever possesses a copy
|
||||
of it. In addition, you must do these things in the Modified Version:
|
||||
|
||||
@enumerate A
|
||||
@item
|
||||
Use in the Title Page (and on the covers, if any) a title distinct
|
||||
from that of the Document, and from those of previous versions
|
||||
(which should, if there were any, be listed in the History section
|
||||
of the Document). You may use the same title as a previous version
|
||||
if the original publisher of that version gives permission.
|
||||
|
||||
@item
|
||||
List on the Title Page, as authors, one or more persons or entities
|
||||
responsible for authorship of the modifications in the Modified
|
||||
Version, together with at least five of the principal authors of the
|
||||
Document (all of its principal authors, if it has fewer than five),
|
||||
unless they release you from this requirement.
|
||||
|
||||
@item
|
||||
State on the Title page the name of the publisher of the
|
||||
Modified Version, as the publisher.
|
||||
|
||||
@item
|
||||
Preserve all the copyright notices of the Document.
|
||||
|
||||
@item
|
||||
Add an appropriate copyright notice for your modifications
|
||||
adjacent to the other copyright notices.
|
||||
|
||||
@item
|
||||
Include, immediately after the copyright notices, a license notice
|
||||
giving the public permission to use the Modified Version under the
|
||||
terms of this License, in the form shown in the Addendum below.
|
||||
|
||||
@item
|
||||
Preserve in that license notice the full lists of Invariant Sections
|
||||
and required Cover Texts given in the Document's license notice.
|
||||
|
||||
@item
|
||||
Include an unaltered copy of this License.
|
||||
|
||||
@item
|
||||
Preserve the section Entitled ``History'', Preserve its Title, and add
|
||||
to it an item stating at least the title, year, new authors, and
|
||||
publisher of the Modified Version as given on the Title Page. If
|
||||
there is no section Entitled ``History'' in the Document, create one
|
||||
stating the title, year, authors, and publisher of the Document as
|
||||
given on its Title Page, then add an item describing the Modified
|
||||
Version as stated in the previous sentence.
|
||||
|
||||
@item
|
||||
Preserve the network location, if any, given in the Document for
|
||||
public access to a Transparent copy of the Document, and likewise
|
||||
the network locations given in the Document for previous versions
|
||||
it was based on. These may be placed in the ``History'' section.
|
||||
You may omit a network location for a work that was published at
|
||||
least four years before the Document itself, or if the original
|
||||
publisher of the version it refers to gives permission.
|
||||
|
||||
@item
|
||||
For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve
|
||||
the Title of the section, and preserve in the section all the
|
||||
substance and tone of each of the contributor acknowledgements and/or
|
||||
dedications given therein.
|
||||
|
||||
@item
|
||||
Preserve all the Invariant Sections of the Document,
|
||||
unaltered in their text and in their titles. Section numbers
|
||||
or the equivalent are not considered part of the section titles.
|
||||
|
||||
@item
|
||||
Delete any section Entitled ``Endorsements''. Such a section
|
||||
may not be included in the Modified Version.
|
||||
|
||||
@item
|
||||
Do not retitle any existing section to be Entitled ``Endorsements'' or
|
||||
to conflict in title with any Invariant Section.
|
||||
|
||||
@item
|
||||
Preserve any Warranty Disclaimers.
|
||||
@end enumerate
|
||||
|
||||
If the Modified Version includes new front-matter sections or
|
||||
appendices that qualify as Secondary Sections and contain no material
|
||||
copied from the Document, you may at your option designate some or all
|
||||
of these sections as invariant. To do this, add their titles to the
|
||||
list of Invariant Sections in the Modified Version's license notice.
|
||||
These titles must be distinct from any other section titles.
|
||||
|
||||
You may add a section Entitled ``Endorsements'', provided it contains
|
||||
nothing but endorsements of your Modified Version by various
|
||||
parties---for example, statements of peer review or that the text has
|
||||
been approved by an organization as the authoritative definition of a
|
||||
standard.
|
||||
|
||||
You may add a passage of up to five words as a Front-Cover Text, and a
|
||||
passage of up to 25 words as a Back-Cover Text, to the end of the list
|
||||
of Cover Texts in the Modified Version. Only one passage of
|
||||
Front-Cover Text and one of Back-Cover Text may be added by (or
|
||||
through arrangements made by) any one entity. If the Document already
|
||||
includes a cover text for the same cover, previously added by you or
|
||||
by arrangement made by the same entity you are acting on behalf of,
|
||||
you may not add another; but you may replace the old one, on explicit
|
||||
permission from the previous publisher that added the old one.
|
||||
|
||||
The author(s) and publisher(s) of the Document do not by this License
|
||||
give permission to use their names for publicity for or to assert or
|
||||
imply endorsement of any Modified Version.
|
||||
|
||||
@item
|
||||
COMBINING DOCUMENTS
|
||||
|
||||
You may combine the Document with other documents released under this
|
||||
License, under the terms defined in section 4 above for modified
|
||||
versions, provided that you include in the combination all of the
|
||||
Invariant Sections of all of the original documents, unmodified, and
|
||||
list them all as Invariant Sections of your combined work in its
|
||||
license notice, and that you preserve all their Warranty Disclaimers.
|
||||
|
||||
The combined work need only contain one copy of this License, and
|
||||
multiple identical Invariant Sections may be replaced with a single
|
||||
copy. If there are multiple Invariant Sections with the same name but
|
||||
different contents, make the title of each such section unique by
|
||||
adding at the end of it, in parentheses, the name of the original
|
||||
author or publisher of that section if known, or else a unique number.
|
||||
Make the same adjustment to the section titles in the list of
|
||||
Invariant Sections in the license notice of the combined work.
|
||||
|
||||
In the combination, you must combine any sections Entitled ``History''
|
||||
in the various original documents, forming one section Entitled
|
||||
``History''; likewise combine any sections Entitled ``Acknowledgements'',
|
||||
and any sections Entitled ``Dedications''. You must delete all
|
||||
sections Entitled ``Endorsements.''
|
||||
|
||||
@item
|
||||
COLLECTIONS OF DOCUMENTS
|
||||
|
||||
You may make a collection consisting of the Document and other documents
|
||||
released under this License, and replace the individual copies of this
|
||||
License in the various documents with a single copy that is included in
|
||||
the collection, provided that you follow the rules of this License for
|
||||
verbatim copying of each of the documents in all other respects.
|
||||
|
||||
You may extract a single document from such a collection, and distribute
|
||||
it individually under this License, provided you insert a copy of this
|
||||
License into the extracted document, and follow this License in all
|
||||
other respects regarding verbatim copying of that document.
|
||||
|
||||
@item
|
||||
AGGREGATION WITH INDEPENDENT WORKS
|
||||
|
||||
A compilation of the Document or its derivatives with other separate
|
||||
and independent documents or works, in or on a volume of a storage or
|
||||
distribution medium, is called an ``aggregate'' if the copyright
|
||||
resulting from the compilation is not used to limit the legal rights
|
||||
of the compilation's users beyond what the individual works permit.
|
||||
When the Document is included in an aggregate, this License does not
|
||||
apply to the other works in the aggregate which are not themselves
|
||||
derivative works of the Document.
|
||||
|
||||
If the Cover Text requirement of section 3 is applicable to these
|
||||
copies of the Document, then if the Document is less than one half of
|
||||
the entire aggregate, the Document's Cover Texts may be placed on
|
||||
covers that bracket the Document within the aggregate, or the
|
||||
electronic equivalent of covers if the Document is in electronic form.
|
||||
Otherwise they must appear on printed covers that bracket the whole
|
||||
aggregate.
|
||||
|
||||
@item
|
||||
TRANSLATION
|
||||
|
||||
Translation is considered a kind of modification, so you may
|
||||
distribute translations of the Document under the terms of section 4.
|
||||
Replacing Invariant Sections with translations requires special
|
||||
permission from their copyright holders, but you may include
|
||||
translations of some or all Invariant Sections in addition to the
|
||||
original versions of these Invariant Sections. You may include a
|
||||
translation of this License, and all the license notices in the
|
||||
Document, and any Warranty Disclaimers, provided that you also include
|
||||
the original English version of this License and the original versions
|
||||
of those notices and disclaimers. In case of a disagreement between
|
||||
the translation and the original version of this License or a notice
|
||||
or disclaimer, the original version will prevail.
|
||||
|
||||
If a section in the Document is Entitled ``Acknowledgements'',
|
||||
``Dedications'', or ``History'', the requirement (section 4) to Preserve
|
||||
its Title (section 1) will typically require changing the actual
|
||||
title.
|
||||
|
||||
@item
|
||||
TERMINATION
|
||||
|
||||
You may not copy, modify, sublicense, or distribute the Document
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense, or distribute it is void, and
|
||||
will automatically terminate your rights under this License.
|
||||
|
||||
However, if you cease all violation of this License, then your license
|
||||
from a particular copyright holder is reinstated (a) provisionally,
|
||||
unless and until the copyright holder explicitly and finally
|
||||
terminates your license, and (b) permanently, if the copyright holder
|
||||
fails to notify you of the violation by some reasonable means prior to
|
||||
60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, receipt of a copy of some or all of the same material does
|
||||
not give you any rights to use it.
|
||||
|
||||
@item
|
||||
FUTURE REVISIONS OF THIS LICENSE
|
||||
|
||||
The Free Software Foundation may publish new, revised versions
|
||||
of the GNU Free Documentation 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. See
|
||||
@uref{http://www.gnu.org/copyleft/}.
|
||||
|
||||
Each version of the License is given a distinguishing version number.
|
||||
If the Document specifies that a particular numbered version of this
|
||||
License ``or any later version'' applies to it, you have the option of
|
||||
following the terms and conditions either of that specified version or
|
||||
of any later version that has been published (not as a draft) by the
|
||||
Free Software Foundation. If the Document does not specify a version
|
||||
number of this License, you may choose any version ever published (not
|
||||
as a draft) by the Free Software Foundation. If the Document
|
||||
specifies that a proxy can decide which future versions of this
|
||||
License can be used, that proxy's public statement of acceptance of a
|
||||
version permanently authorizes you to choose that version for the
|
||||
Document.
|
||||
|
||||
@item
|
||||
RELICENSING
|
||||
|
||||
``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any
|
||||
World Wide Web server that publishes copyrightable works and also
|
||||
provides prominent facilities for anybody to edit those works. A
|
||||
public wiki that anybody can edit is an example of such a server. A
|
||||
``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the
|
||||
site means any set of copyrightable works thus published on the MMC
|
||||
site.
|
||||
|
||||
``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0
|
||||
license published by Creative Commons Corporation, a not-for-profit
|
||||
corporation with a principal place of business in San Francisco,
|
||||
California, as well as future copyleft versions of that license
|
||||
published by that same organization.
|
||||
|
||||
``Incorporate'' means to publish or republish a Document, in whole or
|
||||
in part, as part of another Document.
|
||||
|
||||
An MMC is ``eligible for relicensing'' if it is licensed under this
|
||||
License, and if all works that were first published under this License
|
||||
somewhere other than this MMC, and subsequently incorporated in whole
|
||||
or in part into the MMC, (1) had no cover texts or invariant sections,
|
||||
and (2) were thus incorporated prior to November 1, 2008.
|
||||
|
||||
The operator of an MMC Site may republish an MMC contained in the site
|
||||
under CC-BY-SA on the same site at any time before August 1, 2009,
|
||||
provided the MMC is eligible for relicensing.
|
||||
|
||||
@end enumerate
|
||||
|
||||
@page
|
||||
@heading ADDENDUM: How to use this License for your documents
|
||||
|
||||
To use this License in a document you have written, include a copy of
|
||||
the License in the document and put the following copyright and
|
||||
license notices just after the title page:
|
||||
|
||||
@smallexample
|
||||
@group
|
||||
Copyright (C) @var{year} @var{your name}.
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
or any later version published by the Free Software Foundation;
|
||||
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover
|
||||
Texts. A copy of the license is included in the section entitled ``GNU
|
||||
Free Documentation License''.
|
||||
@end group
|
||||
@end smallexample
|
||||
|
||||
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
|
||||
replace the ``with@dots{}Texts.''@: line with this:
|
||||
|
||||
@smallexample
|
||||
@group
|
||||
with the Invariant Sections being @var{list their titles}, with
|
||||
the Front-Cover Texts being @var{list}, and with the Back-Cover Texts
|
||||
being @var{list}.
|
||||
@end group
|
||||
@end smallexample
|
||||
|
||||
If you have Invariant Sections without Cover Texts, or some other
|
||||
combination of the three, merge those two alternatives to suit the
|
||||
situation.
|
||||
|
||||
If your document contains nontrivial examples of program code, we
|
||||
recommend releasing these examples in parallel under your choice of
|
||||
free software license, such as the GNU General Public License,
|
||||
to permit their use in free software.
|
||||
|
||||
@c Local Variables:
|
||||
@c ispell-local-pdict: "ispell-dict"
|
||||
@c End:
|
||||
1352
doc/mcron.texi
1352
doc/mcron.texi
File diff suppressed because it is too large
Load diff
125
maint.mk
125
maint.mk
|
|
@ -1,125 +0,0 @@
|
|||
## Maintainer-only Makefile fragment
|
||||
# Copyright © 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
#
|
||||
# This file is part of GNU Mcron.
|
||||
#
|
||||
# GNU Mcron is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# GNU Mcron is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Rebuild Makefile.in if this file is modifed.
|
||||
Makefile.in: maint.mk
|
||||
|
||||
## -------------------- ##
|
||||
## Third-party files. ##
|
||||
## ---------------------##
|
||||
|
||||
WGET = wget
|
||||
|
||||
# Git repositories on Savannah.
|
||||
git_sv_host = git.savannah.gnu.org
|
||||
|
||||
# Some repositories we sync files from.
|
||||
sv_git_am = 'https://$(git_sv_host)/gitweb/?p=automake.git;a=blob_plain;hb=HEAD;f='
|
||||
sv_git_gl = 'https://$(git_sv_host)/gitweb/?p=gnulib.git;a=blob_plain;hb=HEAD;f='
|
||||
|
||||
# Files that we fetch and which we compare against.
|
||||
# Note that the 'lib/COPYING' file must still be synced by hand.
|
||||
fetchfiles = \
|
||||
$(sv_git_am)contrib/test-driver.scm \
|
||||
$(sv_git_gl)build-aux/do-release-commit-and-tag \
|
||||
${sv_git_gl}build-aux/gnu-web-doc-update \
|
||||
$(sv_git_gl)build-aux/gnupload
|
||||
|
||||
# Fetch the latest versions of few scripts and files we care about.
|
||||
# A retrieval failure or a copying failure usually mean serious problems,
|
||||
# so we'll just bail out if 'wget' or 'cp' fail.
|
||||
fetch:
|
||||
$(AM_V_at)rm -rf Fetchdir
|
||||
$(AM_V_at)mkdir Fetchdir
|
||||
$(AM_V_GEN)set -e; \
|
||||
if $(AM_V_P); then wget_opts=; else wget_opts=-nv; fi; \
|
||||
for url in $(fetchfiles); do \
|
||||
file=`printf '%s\n' "$$url" | sed 's|^.*/||; s|^.*=||'`; \
|
||||
$(WGET) $$wget_opts "$$url" -O Fetchdir/$$file || exit 1; \
|
||||
if cmp Fetchdir/$$file $(srcdir)/build-aux/$$file >/dev/null; then \
|
||||
: Nothing to do; \
|
||||
else \
|
||||
echo "$@: updating file $$file"; \
|
||||
cp Fetchdir/$$file $(srcdir)/build-aux/$$file || exit 1; \
|
||||
fi; \
|
||||
done
|
||||
$(AM_V_at)rm -rf Fetchdir
|
||||
.PHONY: fetch
|
||||
|
||||
# If it's not already specified, derive the GPG key ID from
|
||||
# the signed tag we've just applied to mark this release.
|
||||
gpg_key_ID = \
|
||||
$$(cd $(srcdir) \
|
||||
&& git cat-file tag v$(VERSION) \
|
||||
| gpgv --status-fd 1 --keyring /dev/null - - 2>/dev/null \
|
||||
| awk '/^\[GNUPG:\] ERRSIG / {print $$3; exit}')
|
||||
|
||||
# Use alpha.gnu.org for alpha and beta releases.
|
||||
# Use ftp.gnu.org for stable releases.
|
||||
gnu_ftp_host-alpha = alpha.gnu.org
|
||||
gnu_ftp_host-beta = alpha.gnu.org
|
||||
gnu_ftp_host-stable = ftp.gnu.org
|
||||
gnu_rel_host = $(gnu_ftp_host-$(release-type))
|
||||
|
||||
noteworthy_changes = * Noteworthy changes in release ?.? (????-??-??) [?]
|
||||
|
||||
.PHONY: release
|
||||
release:
|
||||
cd $(srcdir) && rm -rf autom4te.cache && ./bootstrap && ./configure
|
||||
$(AM_V_at)$(MAKE) Makefile
|
||||
$(AM_V_at)$(srcdir)/build-aux/announce-gen \
|
||||
--mail-headers='To: ??? Mail-Followup-To: $(PACKAGE_BUGREPORT)' \
|
||||
--release-type=$(release-type) \
|
||||
--package=$(PACKAGE) \
|
||||
--prev=`cat .prev-version` \
|
||||
--curr=$(VERSION) \
|
||||
--gpg-key-id=$(gpg_key_ID) \
|
||||
--srcdir=$(srcdir) \
|
||||
--news=$(srcdir)/NEWS \
|
||||
--bootstrap-tools=autoconf,automake,help2man \
|
||||
--no-print-checksums \
|
||||
--url-dir=https://ftp.gnu.org/gnu/$(PACKAGE) \
|
||||
> ~/announce-$(PACKAGE)-$(VERSION)
|
||||
$(AM_V_at)echo $(VERSION) > .prev-version
|
||||
$(AM_V_at)perl -pi \
|
||||
-e '$$. == 3 and print "$(noteworthy_changes)\n\n\n"' \
|
||||
$(srcdir)/NEWS
|
||||
$(AM_V_at)msg=`printf '%s\n' 'maint: Post-release administrivia' '' \
|
||||
'* NEWS: Add header line for next release.' \
|
||||
'* .prev-version: Record previous version.'` || exit 1; \
|
||||
git commit -m "$$msg" -a
|
||||
|
||||
.PHONY: upload
|
||||
upload:
|
||||
$(srcdir)/build-aux/gnupload $(GNUPLOADFLAGS) \
|
||||
--to $(gnu_rel_host):$(PACKAGE) \
|
||||
$(DIST_ARCHIVES)
|
||||
|
||||
.PHONY: web-manual
|
||||
web-manual:
|
||||
$(AM_V_at)cd '$(srcdir)/doc'; \
|
||||
$(SHELL) ../build-aux/gendocs.sh \
|
||||
-o '$(abs_builddir)/doc/manual' \
|
||||
--email $(PACKAGE_BUGREPORT) $(PACKAGE) \
|
||||
"$(PACKAGE_STRING) Reference Manual"
|
||||
$(AM_V_at)echo " *** Upload the doc/manual directory to web-cvs."
|
||||
|
||||
.PHONY: web-manual-update
|
||||
web-manual-update:
|
||||
$(AM_V_GEN)cd $(srcdir) \
|
||||
&& build-aux/gnu-web-doc-update -C $(abs_builddir)
|
||||
53
src/cron.in
53
src/cron.in
|
|
@ -1,53 +0,0 @@
|
|||
#!%GUILE% --no-auto-compile
|
||||
-*- scheme -*-
|
||||
!#
|
||||
|
||||
;;;; cron -- run jobs at scheduled times
|
||||
;;; Copyright © 2003, 2012, 2020 Dale Mellor <mcron-lsfnyl@rdmp.org>
|
||||
;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
(unless (getenv "MCRON_UNINSTALLED")
|
||||
(set! %load-path (cons "%modsrcdir%" %load-path))
|
||||
(set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path)))
|
||||
|
||||
(use-modules (mcron scripts cron)
|
||||
(ice-9 command-line-processor))
|
||||
|
||||
(process-command-line (command-line)
|
||||
application "cron"
|
||||
version "%VERSION%"
|
||||
usage "[OPTIONS]"
|
||||
help-preamble
|
||||
"Unless an option is specified, run a cron daemon as a detached process,"
|
||||
"reading all the information in the usersʼ crontabs and in /etc/crontab."
|
||||
option (--schedule=8 -s string->number
|
||||
"display the next N (or 8) jobs that will be"
|
||||
"run, and exit")
|
||||
option (--noetc -n "do not check /etc/crontab for updates (use"
|
||||
"of this option is HIGHLY RECOMMENDED)")
|
||||
help-postamble
|
||||
"Mandatory or optional arguments to long options are also mandatory or "
|
||||
"optional for any corresponding short options."
|
||||
bug-address "%PACKAGE_BUGREPORT%"
|
||||
copyright
|
||||
"2003, 2012, 2015, 2016, 2018, 2020 Free Software Foundation, Inc."
|
||||
license GPLv3)
|
||||
|
||||
|
||||
(main --schedule --noetc)
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
#!%GUILE% --no-auto-compile
|
||||
-*- scheme -*-
|
||||
!#
|
||||
|
||||
;;;; crontab -- run jobs at scheduled times
|
||||
;;; Copyright © 2003, 2020 Dale Mellor <mcron-lsfnyl@rdmp.org>
|
||||
;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
(unless (getenv "MCRON_UNINSTALLED")
|
||||
(set! %load-path (cons "%modsrcdir%" %load-path))
|
||||
(set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path)))
|
||||
|
||||
(use-modules (mcron scripts crontab)
|
||||
(ice-9 command-line-processor))
|
||||
|
||||
(process-command-line (command-line)
|
||||
application "crontab"
|
||||
version "%VERSION%"
|
||||
usage "[-u user] { -e | -l | -r }"
|
||||
help-preamble "the default operation is to replace, per 1003.2"
|
||||
option (--user= -u "the user whose files are to be manipulated")
|
||||
option (--edit -e "edit this userʼs crontab")
|
||||
option (--list -l "list this userʼs crontab")
|
||||
option (--remove -r "delete the userʼs crontab")
|
||||
bug-address "%PACKAGE_BUGREPORT%"
|
||||
copyright "2003, 2016, 2020 Free Software Foundation, Inc."
|
||||
license GPLv3)
|
||||
|
||||
((@ (mcron scripts crontab) main) --user --edit --list --remove --!)
|
||||
56
src/mcron.in
56
src/mcron.in
|
|
@ -1,56 +0,0 @@
|
|||
#!%GUILE% --no-auto-compile
|
||||
-*- scheme -*-
|
||||
!#
|
||||
|
||||
;;;; mcron -- run jobs at scheduled times
|
||||
;;; Copyright © 2003, 2012, 2020 Dale Mellor <mcron-lsfnyl@rdmp.org>
|
||||
;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
(unless (getenv "MCRON_UNINSTALLED")
|
||||
(set! %load-path (cons "%modsrcdir%" %load-path))
|
||||
(set! %load-compiled-path (cons "%modbuilddir%" %load-compiled-path)))
|
||||
|
||||
(use-modules (mcron scripts mcron)
|
||||
(ice-9 command-line-processor))
|
||||
|
||||
(process-command-line (command-line)
|
||||
application "mcron"
|
||||
version "%VERSION%"
|
||||
usage "[OPTIONS ...] [FILES ...]"
|
||||
help-preamble
|
||||
"Run unattended jobs according to instructions in the FILES... "
|
||||
"(`-' for standard input), or use all the files in ~/.config/cron "
|
||||
"(or the deprecated ~/.cron) with .guile or .vixie extensions.\n"
|
||||
"Note that --daemon and --schedule are mutually exclusive."
|
||||
option (--daemon -d "run as a daemon process")
|
||||
option (--schedule=8 -s string->number
|
||||
"display the next N (or 8) jobs that will be run,"
|
||||
"and then exit")
|
||||
option (--stdin=guile short-i (λ (in) (or (string=? in "guile")
|
||||
(string=? in "vixie")))
|
||||
"format of data passed as standard input or file "
|
||||
"arguments, 'guile' or 'vixie' (default guile)")
|
||||
help-postamble
|
||||
"Mandatory or optional arguments to long options are also mandatory or "
|
||||
"optional for any corresponding short options."
|
||||
bug-address "%PACKAGE_BUGREPORT%"
|
||||
copyright "2003, 2006, 2014, 2020 Free Software Foundation, Inc."
|
||||
license GPLv3)
|
||||
|
||||
(main --daemon --schedule --stdin --!)
|
||||
|
|
@ -1,248 +0,0 @@
|
|||
;;;; base.scm -- core procedures
|
||||
;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;; Copyright © 2016, 2020 Ludovic Courtès <ludo@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;;; Commentary:
|
||||
;;;
|
||||
;;; This module provides the core data structures for scheduling jobs and the
|
||||
;;; procedures for running those jobs.
|
||||
;;;
|
||||
;;;; Code:
|
||||
|
||||
(define-module (mcron base)
|
||||
#:use-module (ice-9 match)
|
||||
#:use-module (ice-9 control)
|
||||
#:use-module (mcron environment)
|
||||
#:use-module (mcron utils)
|
||||
#:use-module (srfi srfi-1)
|
||||
#:use-module (srfi srfi-2)
|
||||
#:use-module (srfi srfi-9)
|
||||
#:use-module (srfi srfi-111)
|
||||
#:export (add-job
|
||||
remove-user-jobs
|
||||
display-schedule
|
||||
run-job-loop
|
||||
;; Deprecated and undocumented procedures.
|
||||
use-system-job-list
|
||||
use-user-job-list
|
||||
clear-system-jobs)
|
||||
#:re-export (clear-environment-mods
|
||||
append-environment-mods))
|
||||
|
||||
;; A cron job.
|
||||
(define-record-type <job>
|
||||
(make-job user time-proc action environment displayable next-time)
|
||||
job?
|
||||
(user job:user) ;object : passwd entry
|
||||
(time-proc job:next-time-function) ;proc : with one 'time' parameter
|
||||
(action job:action) ;thunk : user's code
|
||||
;; Environment variables that need to be set before the ACTION is run.
|
||||
(environment job:environment) ;alist : environment variables
|
||||
(displayable job:displayable) ;string : visible in schedule
|
||||
(next-time job:next-time ;number : time in UNIX format
|
||||
job:next-time-set!))
|
||||
|
||||
;; A schedule of cron jobs.
|
||||
(define-record-type <schedule>
|
||||
;; The schedule is composed of a 'user' and 'system' schedule. This makes
|
||||
;; removing all the jobs belonging to one group easy, which is required for
|
||||
;; full vixie compatibility.
|
||||
(make-schedule user system current)
|
||||
schedule?
|
||||
;; list for jobs that may be placed in '/etc/crontab'.
|
||||
(system schedule-system set-schedule-system!) ;list of <job>
|
||||
;; list for all other jobs.
|
||||
(user schedule-user set-schedule-user!) ;list of <job>
|
||||
(current schedule-current set-schedule-current!)) ;symbol 'user or 'system
|
||||
|
||||
(define %global-schedule
|
||||
;; Global schedule used by 'mcron' and 'cron'.
|
||||
(make-schedule '() '() 'user))
|
||||
|
||||
(define* (use-system-job-list #:key (schedule %global-schedule))
|
||||
"Mutate '%global-schedule' to use system jobs.
|
||||
This procedure is deprecated."
|
||||
(set-schedule-current! schedule 'system))
|
||||
|
||||
(define* (use-user-job-list #:key (schedule %global-schedule))
|
||||
"Mutate '%global-schedule' to use user jobs.
|
||||
This procedure is deprecated."
|
||||
(set-schedule-current! schedule 'user))
|
||||
|
||||
(define* (remove-user-jobs user #:key (schedule %global-schedule))
|
||||
"Remove user jobs from SCHEDULE belonging to USER. USER must be either a
|
||||
username, a UID, or a passwd entry."
|
||||
(let ((user* (get-user user)))
|
||||
(set-schedule-user! schedule
|
||||
(filter (lambda (job)
|
||||
(not (eqv? (passwd:uid user*)
|
||||
(passwd:uid (job:user job)))))
|
||||
(schedule-user schedule)))))
|
||||
|
||||
(define* (clear-system-jobs #:key (schedule %global-schedule))
|
||||
"Remove all the system jobs from SCHEDULE."
|
||||
(set-schedule-system! schedule '()))
|
||||
|
||||
(define* (add-job time-proc action displayable configuration-time
|
||||
configuration-user
|
||||
#:key (schedule %global-schedule))
|
||||
"Add a new job with the given specifications to the current job set in
|
||||
SCHEDULE."
|
||||
(let ((entry (make-job configuration-user
|
||||
time-proc
|
||||
action
|
||||
(get-current-environment-mods-copy)
|
||||
displayable
|
||||
(time-proc configuration-time))))
|
||||
(if (eq? (schedule-current schedule) 'user)
|
||||
(set-schedule-user! schedule (cons entry (schedule-user schedule)))
|
||||
(set-schedule-system! schedule
|
||||
(cons entry (schedule-system schedule))))))
|
||||
|
||||
(define* (find-next-jobs #:key (schedule %global-schedule))
|
||||
"Locate the jobs in SCHEDULE with the lowest (soonest) next-times. Return a
|
||||
list where the head is the next scheduled time and the rest are all the job
|
||||
entries that are to run at this time. When SCHEDULE is empty next time is
|
||||
'#f'."
|
||||
(let loop ((jobs
|
||||
(append (schedule-system schedule) (schedule-user schedule)))
|
||||
(next-time (inf))
|
||||
(next-jobs '()))
|
||||
(match jobs
|
||||
(()
|
||||
(cons (and (finite? next-time) next-time) next-jobs))
|
||||
((job . rest)
|
||||
(let ((this-time (job:next-time job)))
|
||||
(cond ((< this-time next-time)
|
||||
(loop rest this-time (list job)))
|
||||
((= this-time next-time)
|
||||
(loop rest next-time (cons job next-jobs)))
|
||||
(else
|
||||
(loop rest next-time next-jobs))))))))
|
||||
|
||||
(define* (display-schedule count #:optional (port (current-output-port))
|
||||
#:key (schedule %global-schedule))
|
||||
"Display on PORT a textual list of the next COUNT jobs to run. This
|
||||
simulates the run of the job loop to display the requested information.
|
||||
Since calling this procedure has the effect of mutating the job timings, the
|
||||
program must exit after. Otherwise the internal data state will be left
|
||||
unusable."
|
||||
(unless (<= count 0)
|
||||
(match (find-next-jobs #:schedule schedule)
|
||||
((#f . jobs)
|
||||
#f)
|
||||
((time . jobs)
|
||||
(let ((date-string (strftime "%c %z\n" (localtime time))))
|
||||
(for-each (lambda (job)
|
||||
(display date-string port)
|
||||
(display (job:displayable job) port)
|
||||
(newline port)
|
||||
(newline port)
|
||||
(job:next-time-set! job ((job:next-time-function job)
|
||||
(job:next-time job))))
|
||||
jobs))))
|
||||
(display-schedule (- count 1) port #:schedule schedule)))
|
||||
|
||||
;;;
|
||||
;;; Running jobs
|
||||
;;;
|
||||
|
||||
(define number-children
|
||||
;; For proper housekeeping, it is necessary to keep a record of the number
|
||||
;; of child processes we fork off to run the jobs.
|
||||
(box 0))
|
||||
|
||||
(define (update-number-children! proc)
|
||||
;; Apply PROC to the value stored in 'number-children'.
|
||||
(set-box! number-children (proc (unbox number-children))))
|
||||
|
||||
(define (run-job job)
|
||||
"Run JOB in a separate process. The process is run as JOB user with the
|
||||
environment properly set. Update the NEXT-TIME field of JOB by computing its
|
||||
next value."
|
||||
(if (= (primitive-fork) 0)
|
||||
(dynamic-wind ;child
|
||||
(const #t)
|
||||
(λ ()
|
||||
(setgid (passwd:gid (job:user job)))
|
||||
(setuid (passwd:uid (job:user job)))
|
||||
(chdir (passwd:dir (job:user job)))
|
||||
(modify-environment (job:environment job) (job:user job))
|
||||
((job:action job)))
|
||||
(λ ()
|
||||
(primitive-exit 0)))
|
||||
(begin ;parent
|
||||
(update-number-children! 1+)
|
||||
(job:next-time-set! job ((job:next-time-function job)
|
||||
(current-time))))))
|
||||
|
||||
(define (child-cleanup)
|
||||
;; Give any zombie children a chance to die, and decrease the number known
|
||||
;; to exist.
|
||||
(unless (or (<= (unbox number-children) 0)
|
||||
(= (car (waitpid WAIT_ANY WNOHANG)) 0))
|
||||
(update-number-children! 1-)
|
||||
(child-cleanup)))
|
||||
|
||||
(define* (run-job-loop #:optional fd-list #:key (schedule %global-schedule))
|
||||
;; Loop over all job specifications, get a list of the next ones to run (may
|
||||
;; be more than one). Set an alarm and go to sleep. When we wake, run the
|
||||
;; jobs and reap any children (old jobs) that have completed. Repeat ad
|
||||
;; infinitum.
|
||||
;;
|
||||
;; Note that, if we wake ahead of time, it can only mean that a signal has
|
||||
;; been sent by a crontab job to tell us to re-read a crontab file. In this
|
||||
;; case we break out of the loop here, and let the main procedure deal with
|
||||
;; the situation (it will eventually re-call this function, thus maintaining
|
||||
;; the loop).
|
||||
(cond-expand
|
||||
((or guile-3.0 guile-2.2) ;2.2 and 3.0
|
||||
(define select* select))
|
||||
(else
|
||||
;; On Guile 2.0, 'select' could throw upon EINTR or EAGAIN.
|
||||
(define (select* read write except time)
|
||||
(catch 'system-error
|
||||
(lambda ()
|
||||
(select read write except time))
|
||||
(lambda args
|
||||
(if (member (system-error-errno args) (list EAGAIN EINTR))
|
||||
'(() () ())
|
||||
(apply throw args)))))))
|
||||
|
||||
(let/ec break
|
||||
(let loop ()
|
||||
(match (find-next-jobs #:schedule schedule)
|
||||
((next-time . next-jobs-lst)
|
||||
(let ((sleep-time (if next-time
|
||||
(- next-time (current-time))
|
||||
2000000000)))
|
||||
(when (> sleep-time 0)
|
||||
(match (select* fd-list '() '() sleep-time)
|
||||
((() () ())
|
||||
;; 'select' returned an empty set, perhaps because it got
|
||||
;; EINTR or EAGAIN. It's a good time to wait for child
|
||||
;; processes.
|
||||
(child-cleanup))
|
||||
(((lst ...) () ())
|
||||
;; There's some activity so leave the loop.
|
||||
(break))))
|
||||
|
||||
(for-each run-job next-jobs-lst)
|
||||
(child-cleanup)
|
||||
(loop)))))))
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
;;;; config.scm -- variables defined at configure time
|
||||
;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
;;; Copyright © 2015, 2016 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(define-module (mcron config))
|
||||
|
||||
(define-public config-package-name "@PACKAGE_NAME@")
|
||||
(define-public config-package-version "@PACKAGE_VERSION@")
|
||||
(define-public config-package-string "@PACKAGE_STRING@")
|
||||
(define-public config-package-bugreport "@PACKAGE_BUGREPORT@")
|
||||
(define-public config-package-url "@PACKAGE_URL@")
|
||||
(define-public config-sendmail "@SENDMAIL@")
|
||||
|
||||
(define-public config-spool-dir "@CONFIG_SPOOL_DIR@")
|
||||
(define-public config-socket-file "@CONFIG_SOCKET_FILE@")
|
||||
(define-public config-allow-file "@CONFIG_ALLOW_FILE@")
|
||||
(define-public config-deny-file "@CONFIG_DENY_FILE@")
|
||||
(define-public config-pid-file "@CONFIG_PID_FILE@")
|
||||
(define-public config-tmp-dir "@CONFIG_TMP_DIR@")
|
||||
|
||||
;;;
|
||||
;;; Runtime configuration
|
||||
;;;
|
||||
|
||||
(define-public config-debug
|
||||
;; Trigger the display of Guile stack traces on errors.
|
||||
(getenv "MCRON_DEBUG"))
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
;;;; core.scm -- alias for (mcron base) kept for backward compatibility
|
||||
;;; Copyright © 2017 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;; TODO: Deprecate this alias in next major version.
|
||||
|
||||
(define-module (mcron core)
|
||||
#:use-module (mcron base)
|
||||
#:export (;; Deprecated
|
||||
get-schedule)
|
||||
#:re-export (add-job
|
||||
remove-user-jobs
|
||||
run-job-loop
|
||||
clear-environment-mods
|
||||
append-environment-mods
|
||||
;; Deprecated and undocumented procedures.
|
||||
use-system-job-list
|
||||
use-user-job-list
|
||||
clear-system-jobs))
|
||||
|
||||
(define (get-schedule count)
|
||||
(with-output-to-string
|
||||
(lambda () (display-schedule count))))
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
;;;; environment.scm -- interact with the job process environment
|
||||
;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;;; Commentary:
|
||||
;;;
|
||||
;;; Define the variable current-environment-mods, and the procedures
|
||||
;;; append-environment-mods (which is available to user configuration files),
|
||||
;;; clear-environment-mods and modify-environment. The idea is that the
|
||||
;;; current-environment-mods is a list of pairs of environment names and
|
||||
;;; values, and represents the cumulated environment settings in a
|
||||
;;; configuration file. When a job definition is seen in a configuration file,
|
||||
;;; the current-environment-mods are copied into the internal job description,
|
||||
;;; and when the job actually runs these environment modifications are applied
|
||||
;;; to the UNIX environment in which the job runs.
|
||||
;;;
|
||||
;;;; Code:
|
||||
|
||||
(define-module (mcron environment)
|
||||
#:use-module (srfi srfi-111)
|
||||
#:export (modify-environment
|
||||
clear-environment-mods
|
||||
append-environment-mods
|
||||
get-current-environment-mods-copy))
|
||||
|
||||
;;;
|
||||
;;; Configuration files
|
||||
;;;
|
||||
|
||||
(define %current-environment-mods
|
||||
;; Global variable containing an alist of environment variables populated as
|
||||
;; we parse configuration files.
|
||||
(box '()))
|
||||
|
||||
(define* (get-current-environment-mods-copy
|
||||
#:key (environ %current-environment-mods))
|
||||
"Return a snapshot of the current environment modifications from ENVIRON.
|
||||
This snapshot is a copy of the environment so that modifying it doesn't
|
||||
impact ENVIRON."
|
||||
;; Each time a job is registered we should call this procedure.
|
||||
(list-copy (unbox environ)))
|
||||
|
||||
(define* (clear-environment-mods #:key (environ %current-environment-mods))
|
||||
"Remove all entries in the ENVIRON environment."
|
||||
;; When we start to parse a new configuration file, we want to start with a
|
||||
;; fresh environment (actually an umodified version of the pervading mcron
|
||||
;; environment) by calling this procedure.
|
||||
(set-box! environ '()))
|
||||
|
||||
(define* (append-environment-mods name value
|
||||
#:key (environ %current-environment-mods))
|
||||
"Set NAME to VALUE in the ENVIRON environment. If VALUES is #f then NAME is
|
||||
considered unset."
|
||||
;; This procedure is used implicitly by the Vixie parser, and can be used
|
||||
;; directly by users in scheme configuration files.
|
||||
(set-box! environ (append (unbox environ) `((,name . ,value))))
|
||||
;; XXX: The return value is purely for the convenience of the
|
||||
;; '(@ (mcron vixie-specification) parse-vixie-environment)'.
|
||||
#t)
|
||||
|
||||
;;;
|
||||
;;; Job runtime
|
||||
;;;
|
||||
|
||||
(define (modify-environment env passwd-entry)
|
||||
"Modify the environment (in the UNIX sense) by setting the variables from
|
||||
ENV and some default ones which are modulated by PASSWD-ENTRY. \"LOGNAME\"
|
||||
and \"USER\" environment variables can't be overided by ENV. ENV must be an
|
||||
alist which associate environment variables to their value. PASSWD-ENTRY must
|
||||
be an object representing user information which corresponds to a valid entry
|
||||
in /etc/passwd. The return value is not specified."
|
||||
(for-each (lambda (pair) (setenv (car pair) (cdr pair)))
|
||||
(let ((home-dir (passwd:dir passwd-entry))
|
||||
(user-name (passwd:name passwd-entry)))
|
||||
(append
|
||||
;; Default environment variables which can be overided by ENV.
|
||||
`(("HOME" . ,home-dir)
|
||||
("CWD" . ,home-dir)
|
||||
("SHELL" . ,(passwd:shell passwd-entry))
|
||||
("TERM" . #f)
|
||||
("TERMCAP" . #f))
|
||||
env
|
||||
;; Environment variables with imposed values.
|
||||
`(("LOGNAME" . ,user-name)
|
||||
("USER" . ,user-name))))))
|
||||
|
|
@ -1,258 +0,0 @@
|
|||
;;;; job-specifier.scm -- public interface for defining jobs
|
||||
;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
;;; Copyright © 2016, 2017, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;;; Commentary:
|
||||
;;;
|
||||
;;; Define all the functions that can be used by scheme Mcron configuration
|
||||
;;; files, namely the procedures for working out next times, the job procedure
|
||||
;;; for registering new jobs (actually a wrapper around the base add-job
|
||||
;;; function), and the procedure for declaring environment modifications.
|
||||
;;;
|
||||
;;;; Code:
|
||||
|
||||
(define-module (mcron job-specifier)
|
||||
#:use-module (ice-9 match)
|
||||
#:use-module (mcron base)
|
||||
#:use-module (mcron environment)
|
||||
#:use-module (mcron utils)
|
||||
#:use-module (mcron vixie-time)
|
||||
#:use-module (srfi srfi-1)
|
||||
#:use-module (srfi srfi-111)
|
||||
#:re-export (append-environment-mods)
|
||||
#:export (range
|
||||
next-year-from next-year
|
||||
next-month-from next-month
|
||||
next-day-from next-day
|
||||
next-hour-from next-hour
|
||||
next-minute-from next-minute
|
||||
next-second-from next-second
|
||||
set-configuration-user
|
||||
set-configuration-time
|
||||
job))
|
||||
|
||||
(define* (range start end #:optional (step 1))
|
||||
"Produces a list of values from START up to (but not including) END. An
|
||||
optional STEP may be supplied, and (if positive) only every step'th value will
|
||||
go into the list. For example, (range 1 6 2) returns '(1 3 5)."
|
||||
(let ((step* (max step 1)))
|
||||
(unfold (λ (i) (>= i end)) ;predicate
|
||||
identity ;value
|
||||
(λ (i) (+ step* i)) ;next seed
|
||||
start))) ;seed
|
||||
|
||||
(define (%find-best-next current next-list)
|
||||
;; Takes a value and a list of possible next values. It returns a pair
|
||||
;; consisting of the smallest element of the NEXT-LIST, and the smallest
|
||||
;; element larger than the CURRENT value. If an example of the latter
|
||||
;; cannot be found, +INF.0 will be returned.
|
||||
(define (exact-min a b)
|
||||
;; A binary implementation of 'min' which preserves the exactness of its
|
||||
;; arguments.
|
||||
(if (< a b) a b))
|
||||
|
||||
(let loop ((smallest (inf)) (closest+ (inf)) (lst next-list))
|
||||
(match lst
|
||||
(() (cons smallest closest+))
|
||||
((time . rest)
|
||||
(loop (exact-min time smallest)
|
||||
(if (> time current) (exact-min time closest+) closest+)
|
||||
rest)))))
|
||||
|
||||
(define (bump-time time value-list component higher-component
|
||||
set-component! set-higher-component!)
|
||||
;; Return the time corresponding to some near future hour. If hour-list is
|
||||
;; not supplied, the time returned corresponds to the start of the next hour
|
||||
;; of the day.
|
||||
;;
|
||||
;; If the hour-list is supplied the time returned corresponds to the first
|
||||
;; hour of the day in the future which is contained in the list. If all the
|
||||
;; values in the list are less than the current hour, then the time returned
|
||||
;; will correspond to the first hour in the list *on the following day*.
|
||||
;;
|
||||
;; ... except that the function is actually generalized to deal with
|
||||
;; seconds, minutes, etc., in an obvious way :-)
|
||||
(if (null? value-list)
|
||||
(set-component! time (1+ (component time)))
|
||||
(match (%find-best-next (component time) value-list)
|
||||
((smallest . closest+)
|
||||
(cond ((inf? closest+)
|
||||
(set-higher-component! time (1+ (higher-component time)))
|
||||
(set-component! time smallest))
|
||||
(else
|
||||
(set-component! time closest+))))))
|
||||
(first (mktime time)))
|
||||
|
||||
;; Set of configuration methods which use the above general function to bump
|
||||
;; specific components of time to the next legitimate value. In each case, all
|
||||
;; the components smaller than that of interest are taken to zero, so that for
|
||||
;; example the time of the next year will be the time at which the next year
|
||||
;; actually starts.
|
||||
|
||||
(define* (next-year-from current-time #:optional (year-list '()))
|
||||
(let ((time (localtime current-time)))
|
||||
(set-tm:mon time 0)
|
||||
(set-tm:mday time 1)
|
||||
(set-tm:hour time 0)
|
||||
(set-tm:min time 0)
|
||||
(set-tm:sec time 0)
|
||||
(bump-time time year-list tm:year tm:year set-tm:year set-tm:year)))
|
||||
|
||||
(define* (next-month-from current-time #:optional (month-list '()))
|
||||
(let ((time (localtime current-time)))
|
||||
(set-tm:mday time 1)
|
||||
(set-tm:hour time 0)
|
||||
(set-tm:min time 0)
|
||||
(set-tm:sec time 0)
|
||||
(bump-time time month-list tm:mon tm:year set-tm:mon set-tm:year)))
|
||||
|
||||
(define* (next-day-from current-time #:optional (day-list '()))
|
||||
(let ((time (localtime current-time)))
|
||||
(set-tm:hour time 0)
|
||||
(set-tm:min time 0)
|
||||
(set-tm:sec time 0)
|
||||
(bump-time time day-list tm:mday tm:mon set-tm:mday set-tm:mon)))
|
||||
|
||||
(define* (next-hour-from current-time #:optional (hour-list '()))
|
||||
(let ((time (localtime current-time)))
|
||||
(set-tm:min time 0)
|
||||
(set-tm:sec time 0)
|
||||
(bump-time time hour-list tm:hour tm:mday set-tm:hour set-tm:mday)))
|
||||
|
||||
(define* (next-minute-from current-time #:optional (minute-list '()))
|
||||
(let ((time (localtime current-time)))
|
||||
(set-tm:sec time 0)
|
||||
(bump-time time minute-list tm:min tm:hour set-tm:min set-tm:hour)))
|
||||
|
||||
(define* (next-second-from current-time #:optional (second-list '()))
|
||||
(let ((time (localtime current-time)))
|
||||
(bump-time time second-list tm:sec tm:min set-tm:sec set-tm:min)))
|
||||
|
||||
;;; The following procedures are convenient for configuration files. They are
|
||||
;;; wrappers for the next-X-from functions above, by implicitly using
|
||||
;;; %CURRENT-ACTION-TIME as the time argument.
|
||||
|
||||
(define %current-action-time
|
||||
;; The time a job was last run, the time from which the next time to run a
|
||||
;; job must be computed. (When the program is first run, this time is set to
|
||||
;; the configuration time so that jobs run from that moment forwards.) Once
|
||||
;; we have this, we supply versions of the time computation commands above
|
||||
;; which implicitly assume this value.
|
||||
(make-parameter 0))
|
||||
|
||||
(define* (next-year #:optional (args '()))
|
||||
"Compute the next year from %CURRENT-ACTION-TIME parameter object."
|
||||
(next-year-from (%current-action-time) args))
|
||||
|
||||
(define* (next-month #:optional (args '()))
|
||||
"Compute the next month from %CURRENT-ACTION-TIME parameter object."
|
||||
(next-month-from (%current-action-time) args))
|
||||
|
||||
(define* (next-day #:optional (args '()))
|
||||
"Compute the next day from %CURRENT-ACTION-TIME parameter object."
|
||||
(next-day-from (%current-action-time) args))
|
||||
|
||||
(define* (next-hour #:optional (args '()))
|
||||
"Compute the next hour from %CURRENT-ACTION-TIME parameter object."
|
||||
(next-hour-from (%current-action-time) args))
|
||||
|
||||
(define* (next-minute #:optional (args '()))
|
||||
"Compute the next minute from %CURRENT-ACTION-TIME parameter object."
|
||||
(next-minute-from (%current-action-time) args))
|
||||
|
||||
(define* (next-second #:optional (args '()))
|
||||
"Compute the next second from %CURRENT-ACTION-TIME parameter object."
|
||||
(next-second-from (%current-action-time) args))
|
||||
|
||||
;; The default user for running jobs is the current one (who invoked this
|
||||
;; program). There are exceptions: when cron parses /etc/crontab the user is
|
||||
;; specified on each individual line; when cron parses /var/cron/tabs/* the user
|
||||
;; is derived from the filename of the crontab. These cases are dealt with by
|
||||
;; mutating this variable. Note that the variable is only used at configuration
|
||||
;; time; a UID is stored with each job and it is that which takes effect when
|
||||
;; the job actually runs.
|
||||
|
||||
(define configuration-user (box (getpw (getuid))))
|
||||
|
||||
(define configuration-time
|
||||
;; Use SOURCE_DATE_EPOCH environment variable to support reproducible tests.
|
||||
(if (getenv "SOURCE_DATE_EPOCH") 0 (current-time)))
|
||||
|
||||
(define (set-configuration-user user)
|
||||
(set-box! configuration-user (get-user user)))
|
||||
(define (set-configuration-time time) (set! configuration-time time))
|
||||
|
||||
|
||||
|
||||
;; The job function, available to configuration files for adding a job rule to
|
||||
;; the system.
|
||||
;;
|
||||
;; Here we must 'normalize' the next-time-function so that it is always a
|
||||
;; lambda function which takes one argument (the last time the job ran) and
|
||||
;; returns a single value (the next time the job should run). If the input
|
||||
;; value is a string this is parsed as a Vixie-style time specification, and
|
||||
;; if it is a list then we arrange to eval it (but note that such lists are
|
||||
;; expected to ignore the function parameter - the last run time is always
|
||||
;; read from the %CURRENT-ACTION-TIME parameter object). A similar
|
||||
;; normalization is applied to the action.
|
||||
;;
|
||||
;; Here we also compute the first time that the job is supposed to run, by
|
||||
;; finding the next legitimate time from the current configuration time (set
|
||||
;; right at the top of this program).
|
||||
|
||||
(define* (job time-proc action #:optional displayable
|
||||
#:key (user (unbox configuration-user)))
|
||||
(let ((action (cond ((procedure? action) action)
|
||||
((list? action) (lambda () (primitive-eval action)))
|
||||
((string? action) (lambda () (system action)))
|
||||
(else
|
||||
(throw 'mcron-error 2
|
||||
"job: invalid second argument (action; should be lambda "
|
||||
"function, string or list)"))))
|
||||
|
||||
(time-proc
|
||||
(cond ((procedure? time-proc) time-proc)
|
||||
((string? time-proc) (parse-vixie-time time-proc))
|
||||
((list? time-proc) (lambda (current-time)
|
||||
(eval time-proc
|
||||
(resolve-module '(mcron job-specifier)))))
|
||||
(else
|
||||
(throw 'mcron-error 3
|
||||
"job: invalid first argument (next-time-function; "
|
||||
"should be function, string or list)"))))
|
||||
(displayable
|
||||
(cond (displayable displayable)
|
||||
((procedure? action) "Lambda function")
|
||||
((string? action) action)
|
||||
((list? action) (simple-format #f "~A" action))))
|
||||
(user* (get-user user)))
|
||||
(add-job (lambda (current-time)
|
||||
(parameterize ((%current-action-time current-time))
|
||||
;; Allow for daylight savings time changes.
|
||||
(let* ((next (time-proc current-time))
|
||||
(gmtoff (tm:gmtoff (localtime next)))
|
||||
(d (+ next
|
||||
(- gmtoff
|
||||
(tm:gmtoff (localtime current-time))))))
|
||||
(if (eqv? (tm:gmtoff (localtime d)) gmtoff)
|
||||
d
|
||||
next))))
|
||||
action
|
||||
displayable
|
||||
configuration-time
|
||||
user*)))
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
;;;; redirect.scm -- modify job outputs
|
||||
;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
;;; Copyright © 2020 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;; Copyright © 2018 宋文武 <iyzsong@member.fsf.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;;; Commentary:
|
||||
;;;
|
||||
;;; Provide the (with-mail-out action . user) procedure. This procedure runs
|
||||
;;; the action in a child process, allowing the user control over the input
|
||||
;;; and output (including standard error). The input is governed (only in the
|
||||
;;; case of a string action) by the placing of percentage signs in the string;
|
||||
;;; the first delimits the true action from the standard input, and subsequent
|
||||
;;; ones denote newlines to be placed into the input. The output (if there
|
||||
;;; actually is any) is controlled by the MAILTO environment variable. If
|
||||
;;; this is not defined, output is e-mailed to the user passed as argument, if
|
||||
;;; any, or else the owner of the action; if defined but empty then any output
|
||||
;;; is sunk to /dev/null; otherwise output is e-mailed to the address held in
|
||||
;;; the MAILTO variable.
|
||||
;;;
|
||||
;;;; Code:
|
||||
|
||||
(define-module (mcron redirect)
|
||||
#:use-module (ice-9 popen)
|
||||
#:use-module (ice-9 regex)
|
||||
#:use-module (mcron config)
|
||||
#:use-module (mcron vixie-time)
|
||||
#:export (with-mail-out))
|
||||
|
||||
;; An action string consists of a sequence of characters forming a command
|
||||
;; executable by the shell, possibly followed by an non-escaped percentage
|
||||
;; sign. The text after the percentage sign is to be fed to the command's
|
||||
;; standard input, with further unescaped percents being substituted with
|
||||
;; newlines. The escape character can itself be escaped.
|
||||
;;
|
||||
;; This regexp separates the two halves of the string, and indeed determines if
|
||||
;; the second part is present.
|
||||
|
||||
(define action-string-regexp (make-regexp "((\\\\%|[^%])*)%(.*)$"))
|
||||
|
||||
|
||||
|
||||
;; This regexp identifies an escaped percentage sign.
|
||||
|
||||
(define e-percent (make-regexp "\\\\%"))
|
||||
|
||||
|
||||
;; Function to execute some action (this may be a shell command, lamdba function
|
||||
;; or list of scheme procedures) in a forked process, with the input coming from
|
||||
;; the string, and output (including the error output) being sent to a pipe
|
||||
;; opened on a mail transport.
|
||||
|
||||
(define* (with-mail-out action #:optional user #:key
|
||||
(hostname (gethostname))
|
||||
(out (lambda ()
|
||||
(open-output-pipe config-sendmail))))
|
||||
|
||||
;; Determine the name of the user who is to recieve the mail, looking for a
|
||||
;; name in the optional user argument, then in the MAILTO environment
|
||||
;; variable, and finally in the LOGNAME environment variable. (The case
|
||||
;; MAILTO="" is dealt with specially below.)
|
||||
|
||||
(let* ((mailto (getenv "MAILTO"))
|
||||
(user (cond (mailto mailto)
|
||||
(user user)
|
||||
(else (getenv "LOGNAME"))))
|
||||
(parent->child (pipe))
|
||||
(child->parent (pipe))
|
||||
(child-pid (primitive-fork)))
|
||||
|
||||
|
||||
;; The child process. Close redundant ends of pipes, remap the standard
|
||||
;; streams, and run the action, taking care to chop off the input part of an
|
||||
;; action string.
|
||||
|
||||
(if (eqv? child-pid 0)
|
||||
(begin
|
||||
(close (cdr parent->child))
|
||||
(close (car child->parent))
|
||||
|
||||
(dup2 (port->fdes (car parent->child)) 0)
|
||||
(close (car parent->child))
|
||||
(dup2 (port->fdes (cdr child->parent)) 1)
|
||||
(close (cdr child->parent))
|
||||
(dup2 1 2)
|
||||
|
||||
(cond ((string? action)
|
||||
(let ((match (regexp-exec action-string-regexp action)))
|
||||
(system (if match
|
||||
(let ((action (match:substring match 1)))
|
||||
(do ((match (regexp-exec e-percent action)
|
||||
(regexp-exec e-percent action)))
|
||||
((not match))
|
||||
(set! action (string-append
|
||||
(match:prefix match)
|
||||
"%"
|
||||
(match:suffix match))))
|
||||
action)
|
||||
action))))
|
||||
|
||||
((procedure? action) (action))
|
||||
((list? action) (primitive-eval action)))
|
||||
|
||||
(primitive-exit 0)))
|
||||
|
||||
|
||||
;; The parent process. Get rid of redundant pipe ends.
|
||||
|
||||
(close (car parent->child))
|
||||
(close (cdr child->parent))
|
||||
|
||||
|
||||
;; Put stuff to child from after '%' in command line, replacing
|
||||
;; other %'s with newlines. Ugly or what?
|
||||
|
||||
(if (string? action)
|
||||
(let ((port (cdr parent->child))
|
||||
(match (regexp-exec action-string-regexp action)))
|
||||
(if (and match
|
||||
(match:substring match 3))
|
||||
(with-input-from-string (match:substring match 3)
|
||||
(lambda ()
|
||||
(let loop ()
|
||||
(let ((next-char (read-char)))
|
||||
(if (not (eof-object? next-char))
|
||||
(cond
|
||||
((char=? next-char #\%)
|
||||
(newline port)
|
||||
(loop))
|
||||
((char=? next-char #\\)
|
||||
(let ((escape (read-char)))
|
||||
(if (eof-object? escape)
|
||||
(display #\\ port)
|
||||
(if (char=? escape #\%)
|
||||
(begin
|
||||
(display #\% port)
|
||||
(loop))
|
||||
(begin
|
||||
(display #\\ port)
|
||||
(display escape port)
|
||||
(loop))))))
|
||||
(else
|
||||
(display next-char port)
|
||||
(loop)))))))))))
|
||||
|
||||
|
||||
;; So the child process doesn't hang on to its input expecting more stuff.
|
||||
|
||||
(close (cdr parent->child))
|
||||
|
||||
|
||||
;; That's got streaming into the child's input out of the way, now we stream
|
||||
;; the child's output to a mail sink, but only if there is something there
|
||||
;; in the first place.
|
||||
|
||||
(if (eof-object? (peek-char (car child->parent)))
|
||||
|
||||
(read-char (car child->parent))
|
||||
|
||||
(begin
|
||||
(set-current-output-port (if (and (string? mailto)
|
||||
(string=? mailto ""))
|
||||
(open-output-file "/dev/null")
|
||||
;; The sendmail command should read
|
||||
;; recipients from the message header.
|
||||
(out)))
|
||||
(set-current-input-port (car child->parent))
|
||||
(display "To: ") (display user) (newline)
|
||||
(display "From: mcron") (newline)
|
||||
(display (string-append "Subject: " user "@" hostname))
|
||||
(newline)
|
||||
(newline)
|
||||
|
||||
(do ((next-char (read-char) (read-char)))
|
||||
((eof-object? next-char))
|
||||
(display next-char))))
|
||||
|
||||
(close (car child->parent))
|
||||
|
||||
(waitpid child-pid)))
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
;;;; cron -- daemon for running jobs at scheduled times
|
||||
;;; Copyright © 2003, 2012 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
(define-module (mcron scripts cron)
|
||||
#:use-module (ice-9 ftw)
|
||||
#:use-module (mcron base)
|
||||
#:use-module (mcron config)
|
||||
#:use-module (mcron job-specifier)
|
||||
#:use-module (mcron utils)
|
||||
#:use-module (mcron vixie-specification)
|
||||
#:use-module (srfi srfi-2)
|
||||
#:export (main))
|
||||
|
||||
|
||||
|
||||
(define (delete-run-file)
|
||||
"Remove the /var/run/cron.pid file so that crontab and other invocations of
|
||||
cron don't get the wrong idea that a daemon is currently running. This
|
||||
procedure is called from the C front-end whenever a terminal signal is
|
||||
received."
|
||||
(catch #t
|
||||
(λ ()
|
||||
(delete-file config-pid-file)
|
||||
(delete-file config-socket-file))
|
||||
noop)
|
||||
(quit))
|
||||
|
||||
|
||||
|
||||
(define (cron-file-descriptors)
|
||||
"Establish a socket to listen for updates from a crontab program, and return
|
||||
a list containing the file descriptors correponding to the files read by
|
||||
crontab. This requires that command-type is 'cron."
|
||||
(catch #t
|
||||
(λ ()
|
||||
(let ((sock (socket AF_UNIX SOCK_STREAM 0)))
|
||||
(bind sock AF_UNIX config-socket-file)
|
||||
(listen sock 5)
|
||||
(list sock)))
|
||||
(λ (key . args)
|
||||
(delete-file config-pid-file)
|
||||
(mcron-error 1 "Cannot bind to UNIX socket " config-socket-file))))
|
||||
|
||||
|
||||
|
||||
(define (process-files-in-system-directory)
|
||||
"Process all the files in the crontab directory. When the job procedure is
|
||||
run on behalf of the configuration files, the jobs are registered on the
|
||||
system with the appropriate user. Only root should be able to perform this
|
||||
operation. The permissions on the /var/cron/tabs directory enforce this."
|
||||
|
||||
(define (user-entry name)
|
||||
;; Return the user database entry if NAME is valid, otherwise #f.
|
||||
(false-if-exception (getpwnam name)))
|
||||
|
||||
(catch #t
|
||||
(λ ()
|
||||
(for-each
|
||||
(λ (user)
|
||||
(and-let* ((entry (user-entry user))) ;crontab without user?
|
||||
(set-configuration-user entry)
|
||||
(catch-mcron-error
|
||||
(read-vixie-file (string-append config-spool-dir "/" user)))))
|
||||
(scandir config-spool-dir)))
|
||||
(λ (key . args)
|
||||
(mcron-error 4
|
||||
"You do not have permission to access the system crontabs."))))
|
||||
|
||||
(define (%process-files noetc?)
|
||||
;; Clear MAILTO so that outputs are sent to the various users.
|
||||
(setenv "MAILTO" #f)
|
||||
;; Having defined all the necessary procedures for scanning various sets of
|
||||
;; files, we perform the actual configuration of the program depending on
|
||||
;; the personality we are running as. If it is mcron, we either scan the
|
||||
;; files passed on the command line, or else all the ones in the user's
|
||||
;; .config/cron (or .cron) directory. If we are running under the cron
|
||||
;; personality, we read the /var/cron/tabs directory and also the
|
||||
;; /etc/crontab file.
|
||||
(process-files-in-system-directory)
|
||||
(use-system-job-list)
|
||||
(catch-mcron-error
|
||||
(read-vixie-file "/etc/crontab" parse-system-vixie-line))
|
||||
(use-user-job-list)
|
||||
(unless noetc?
|
||||
(display "\
|
||||
WARNING: cron will check for updates to /etc/crontab EVERY MINUTE. If you do
|
||||
not use this file, or you are prepared to manually restart cron whenever you
|
||||
make a change, then it is HIGHLY RECOMMENDED that you use the --noetc
|
||||
option.\n")
|
||||
(set-configuration-user "root")
|
||||
(job '(- (next-minute-from (next-minute)) 6)
|
||||
check-system-crontab
|
||||
"/etc/crontab update checker.")))
|
||||
|
||||
|
||||
;;;
|
||||
;;; Entry point.
|
||||
;;;
|
||||
|
||||
(define (main --schedule --noetc)
|
||||
(when config-debug (debug-enable 'backtrace))
|
||||
|
||||
(cond ((not (zero? (getuid)))
|
||||
(mcron-error 16
|
||||
"This program must be run by the root user (and should"
|
||||
" have been installed as such)."))
|
||||
((access? config-pid-file F_OK)
|
||||
(mcron-error 1
|
||||
"A cron daemon is already running.\n (If you are sure"
|
||||
" this is not true, remove the file\n "
|
||||
config-pid-file ".)"))
|
||||
(else
|
||||
(cond (--schedule
|
||||
=> (λ (count)
|
||||
(display-schedule (max 1 (string->number count)))
|
||||
(exit 0))))
|
||||
(%process-files --noetc)))
|
||||
|
||||
;; Daemonize ourself.
|
||||
(unless (eq? 0 (primitive-fork)) (exit 0))
|
||||
(setsid)
|
||||
|
||||
;; Set up process signal handlers, as signals are the only way to terminate
|
||||
;; the daemon and we MUST be graceful in defeat.
|
||||
(for-each (λ (x) (sigaction x
|
||||
(λ (sig) (catch #t
|
||||
(λ ()
|
||||
(delete-file config-pid-file)
|
||||
(delete-file config-socket-file))
|
||||
noop)
|
||||
(exit EXIT_FAILURE))))
|
||||
'(SIGTERM SIGINT SIGQUIT SIGHUP))
|
||||
|
||||
;; We can now write the PID file.
|
||||
(with-output-to-file config-pid-file
|
||||
(λ () (display (getpid)) (newline)))
|
||||
|
||||
;; Forever execute the 'run-job-loop', and when it drops out (can
|
||||
;; only be because a message has come in on the socket) we
|
||||
;; process the socket request before restarting the loop again.
|
||||
(catch-mcron-error
|
||||
(let ((fdes-list (cron-file-descriptors)))
|
||||
(while #t
|
||||
(run-job-loop fdes-list)
|
||||
(unless (null? fdes-list) (process-update-request fdes-list))))))
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
;;;; crontab -- edit user's cron tabs
|
||||
;;; Copyright © 2003, 2004 Dale Mellor <>
|
||||
;;; Copyright © 2016 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(define-module (mcron scripts crontab)
|
||||
#:use-module (ice-9 rdelim)
|
||||
#:use-module (mcron config)
|
||||
#:use-module (mcron utils)
|
||||
#:use-module (mcron vixie-specification)
|
||||
#:export (main))
|
||||
|
||||
(define (hit-server user-name)
|
||||
"Tell the running cron daemon that the user corresponding to
|
||||
USER-NAME has modified his crontab. USER-NAME is written to the
|
||||
'/var/cron/socket' UNIX socket."
|
||||
(catch #t
|
||||
(lambda ()
|
||||
(let ((socket (socket AF_UNIX SOCK_STREAM 0)))
|
||||
(connect socket AF_UNIX config-socket-file)
|
||||
(display user-name socket)
|
||||
(close socket)))
|
||||
(lambda (key . args)
|
||||
(display "Warning: a cron daemon is not running.\n"))))
|
||||
|
||||
|
||||
|
||||
;; Display the prompt and wait for user to type his choice. Return #t if the
|
||||
;; answer begins with 'y' or 'Y', return #f if it begins with 'n' or 'N',
|
||||
;; otherwise ask again.
|
||||
(define (get-yes-no prompt . re-prompt)
|
||||
(unless (null? re-prompt)
|
||||
(display "Please answer y or n.\n"))
|
||||
(display (string-append prompt " "))
|
||||
(let ((r (read-line)))
|
||||
(if (not (string-null? r))
|
||||
(case (string-ref r 0)
|
||||
((#\y #\Y) #t)
|
||||
((#\n #\N) #f)
|
||||
(else (get-yes-no prompt #t)))
|
||||
(get-yes-no prompt #t))))
|
||||
|
||||
|
||||
|
||||
(define (in-access-file? file name)
|
||||
"Scan FILE which should contain one user name per line (such as
|
||||
'/var/cron/allow' and '/var/cron/deny'). Return #t if NAME is in there, and
|
||||
#f otherwise. if FILE cannot be opened, a error is signaled."
|
||||
(catch #t
|
||||
(lambda ()
|
||||
(with-input-from-file file
|
||||
(lambda ()
|
||||
(let loop ((input (read-line)))
|
||||
(cond ((eof-object? input)
|
||||
#f)
|
||||
((string=? input name)
|
||||
#t)
|
||||
(else
|
||||
(loop (read-line))))))))
|
||||
(const '())))
|
||||
|
||||
|
||||
;;;
|
||||
;;; Entry point.
|
||||
;;;
|
||||
|
||||
(define (main --user --edit --list --remove files)
|
||||
(when config-debug (debug-enable 'backtrace))
|
||||
(let ((crontab-real-user
|
||||
;; This program should have been installed SUID root. Here we get
|
||||
;; the passwd entry for the real user who is running this program.
|
||||
(passwd:name (getpw (getuid)))))
|
||||
|
||||
;; If the real user is not allowed to use crontab due to the
|
||||
;; /var/cron/allow and/or /var/cron/deny files, bomb out now.
|
||||
(if (or (eq? (in-access-file? config-allow-file crontab-real-user) #f)
|
||||
(eq? (in-access-file? config-deny-file crontab-real-user) #t))
|
||||
(mcron-error 6 "Access denied by system operator."))
|
||||
|
||||
;; Check that no more than one of the mutually exclusive options are
|
||||
;; being used.
|
||||
(when (< 1 (+ (if --edit 1 0) (if --list 1 0) (if --remove 1 0)))
|
||||
(mcron-error 7 "Only one of options -e, -l or -r can be used."))
|
||||
|
||||
;; Check that a non-root user is trying to read someone else's files.
|
||||
(when (and (not (zero? (getuid))) --user)
|
||||
(mcron-error 8 "Only root can use the -u option."))
|
||||
|
||||
(letrec* (;; Iff the --user option is given, the crontab-user may be
|
||||
;; different from the real user.
|
||||
(crontab-user (or --user crontab-real-user))
|
||||
;; So now we know which crontab file we will be manipulating.
|
||||
(crontab-file
|
||||
(string-append config-spool-dir "/" crontab-user)))
|
||||
;; There are four possible sub-personalities to the crontab
|
||||
;; personality: list, remove, edit and replace (when the user uses no
|
||||
;; options but supplies file names on the command line).
|
||||
(cond
|
||||
;; In the list personality, we simply open the crontab and copy it
|
||||
;; character-by-character to the standard output. If anything goes
|
||||
;; wrong, it can only mean that this user does not have a crontab
|
||||
;; file.
|
||||
(--list
|
||||
(catch #t
|
||||
(λ ()
|
||||
(with-input-from-file crontab-file
|
||||
(λ ()
|
||||
(do ((input (read-char) (read-char)))
|
||||
((eof-object? input))
|
||||
(display input)))))
|
||||
(λ (key . args)
|
||||
(display (string-append "No crontab for "
|
||||
crontab-user
|
||||
" exists.\n")))))
|
||||
|
||||
;; In the edit personality, we determine the name of a temporary file
|
||||
;; and an editor command, copy an existing crontab file (if it is
|
||||
;; there) to the temporary file, making sure the ownership is set so
|
||||
;; the real user can edit it; once the editor returns we try to read
|
||||
;; the file to check that it is parseable (but do nothing more with
|
||||
;; the configuration), and if it is okay (this program is still
|
||||
;; running!) we move the temporary file to the real crontab, wake the
|
||||
;; cron daemon up, and remove the temporary file. If the parse fails,
|
||||
;; we give user a choice of editing the file again or quitting the
|
||||
;; program and losing all changes made.
|
||||
(--edit
|
||||
(let ((temp-file (string-append config-tmp-dir
|
||||
"/crontab."
|
||||
(number->string (getpid)))))
|
||||
(catch #t
|
||||
(λ () (copy-file crontab-file temp-file))
|
||||
(λ (key . args) (with-output-to-file temp-file noop)))
|
||||
(chown temp-file (getuid) (getgid))
|
||||
(let retry ()
|
||||
(system (string-append
|
||||
(or (getenv "VISUAL") (getenv "EDITOR") "vi")
|
||||
" "
|
||||
temp-file))
|
||||
(catch 'mcron-error
|
||||
(λ () (read-vixie-file temp-file))
|
||||
(λ (key exit-code . msg)
|
||||
(apply mcron-error 0 msg)
|
||||
(if (get-yes-no "Edit again?")
|
||||
(retry)
|
||||
(begin
|
||||
(mcron-error 0 "Crontab not changed")
|
||||
(primitive-exit 0))))))
|
||||
(copy-file temp-file crontab-file)
|
||||
(delete-file temp-file)
|
||||
(hit-server crontab-user)))
|
||||
|
||||
;; In the remove personality we simply make an effort to delete the
|
||||
;; crontab and wake the daemon. No worries if this fails.
|
||||
(--remove (catch #t (λ () (delete-file crontab-file)
|
||||
(hit-server crontab-user))
|
||||
noop))
|
||||
|
||||
;; XXX: This comment is wrong.
|
||||
;; In the case of the replace personality we loop over all the
|
||||
;; arguments on the command line, and for each one parse the file to
|
||||
;; make sure it is parseable (but subsequently ignore the
|
||||
;; configuration), and all being well we copy it to the crontab
|
||||
;; location; we deal with the standard input in the same way but
|
||||
;; different. :-) In either case the server is woken so that it will
|
||||
;; read the newly installed crontab.
|
||||
((not (null? files))
|
||||
(let ((input-file (car files)))
|
||||
(catch-mcron-error
|
||||
(if (string=? input-file "-")
|
||||
(let ((input-string (read-string)))
|
||||
(read-vixie-port (open-input-string input-string))
|
||||
(with-output-to-file crontab-file
|
||||
(λ () (display input-string))))
|
||||
(begin
|
||||
(read-vixie-file input-file)
|
||||
(copy-file input-file crontab-file))))
|
||||
(hit-server crontab-user)))
|
||||
|
||||
;; The user is being silly. The message here is identical to the one
|
||||
;; Vixie cron used to put out, for total compatibility.
|
||||
(else (mcron-error 15
|
||||
"usage error: file name must be specified for replace."))))))
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
;;;; mcron -- run jobs at scheduled times
|
||||
;;; Copyright © 2003, 2012, 2020 Dale Mellor <>
|
||||
;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(define-module (mcron scripts mcron)
|
||||
#:use-module (ice-9 ftw)
|
||||
#:use-module (ice-9 local-eval)
|
||||
#:use-module (ice-9 rdelim)
|
||||
#:use-module (mcron base)
|
||||
#:use-module (mcron config)
|
||||
#:use-module (mcron job-specifier) ; For user/system files.
|
||||
#:use-module (mcron utils)
|
||||
#:use-module (mcron vixie-specification)
|
||||
#:export (main))
|
||||
|
||||
|
||||
|
||||
(define process-user-file
|
||||
(let ((guile-regexp (make-regexp "\\.gui(le)?$"))
|
||||
(vixie-regexp (make-regexp "\\.vix(ie)?$")))
|
||||
(lambda* (file-name #:optional guile-syntax? #:key (input "guile"))
|
||||
"Process FILE-NAME according its extension. When GUILE-SYNTAX? is TRUE,
|
||||
force guile syntax usage. If FILE-NAME format is not recognized, it is
|
||||
silently ignored."
|
||||
(cond ((string=? "-" file-name)
|
||||
(if (string=? input "vixie")
|
||||
(read-vixie-port (current-input-port))
|
||||
(eval-string (read-string)
|
||||
(resolve-module '(mcron job-specifier)))))
|
||||
((or guile-syntax? (regexp-exec guile-regexp file-name))
|
||||
(eval-string (read-delimited "" (open-input-file file-name))
|
||||
(resolve-module '(mcron job-specifier))))
|
||||
((regexp-exec vixie-regexp file-name)
|
||||
(read-vixie-file file-name))))))
|
||||
|
||||
|
||||
|
||||
(define (process-files-in-user-directory input-type)
|
||||
"Process files in $XDG_CONFIG_HOME/cron and/or ~/.cron directories (if
|
||||
$XDG_CONFIG_HOME is not defined uses ~/.config/cron instead)."
|
||||
(let ((errors 0)
|
||||
(home-directory (passwd:dir (getpw (getuid)))))
|
||||
(map (λ (dir)
|
||||
(catch #t
|
||||
(λ ()
|
||||
(for-each (λ (file)
|
||||
(process-user-file (string-append dir "/" file)
|
||||
#:input input-type))
|
||||
(scandir dir)))
|
||||
(λ (key . args)
|
||||
(set! errors (1+ errors)))))
|
||||
(list (string-append home-directory "/.cron")
|
||||
(string-append (or (getenv "XDG_CONFIG_HOME")
|
||||
(string-append home-directory "/.config"))
|
||||
"/cron")))
|
||||
(when (eq? 2 errors)
|
||||
(mcron-error 13
|
||||
"Cannot read files in your ~/.config/cron (or ~/.cron) directory."))))
|
||||
|
||||
|
||||
|
||||
(define (%process-files files input-type)
|
||||
(if (null? files)
|
||||
(process-files-in-user-directory input-type)
|
||||
(for-each (λ (file) (process-user-file file #t)) files)))
|
||||
|
||||
|
||||
;;;
|
||||
;;; Entry point.
|
||||
;;;
|
||||
|
||||
(define (main --daemon --schedule --stdin file-list)
|
||||
|
||||
(when config-debug (debug-enable 'backtrace))
|
||||
(%process-files file-list (or --stdin "guile"))
|
||||
(cond (--schedule
|
||||
=> (λ (count)
|
||||
(display-schedule
|
||||
(max 1 (inexact->exact (floor (string->number count)))))
|
||||
(exit 0)))
|
||||
(--daemon (case (primitive-fork) ((0) (setsid))
|
||||
(else (exit 0)))))
|
||||
|
||||
;; Forever execute the 'run-job-loop', and when it drops out (can only be
|
||||
;; because a message has come in on the socket) we process the socket
|
||||
;; request before restarting the loop again.
|
||||
(catch-mcron-error
|
||||
(let ((fdes-list '()))
|
||||
(while #t
|
||||
(run-job-loop fdes-list)
|
||||
;; we can also drop out of run-job-loop because of a SIGCHLD,
|
||||
;; so must test FDES-LIST.
|
||||
(unless (null? fdes-list)
|
||||
(process-update-request fdes-list))))))
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
;;;; utils.scm -- helper procedures
|
||||
;;; Copyright © 2003, 2012 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
;;; Copyright © 2015, 2016, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(define-module (mcron utils)
|
||||
#:use-module (ice-9 rdelim)
|
||||
#:use-module (mcron config)
|
||||
#:use-module (mcron base)
|
||||
#:use-module (mcron job-specifier)
|
||||
#:use-module (mcron vixie-specification)
|
||||
#:export (catch-mcron-error
|
||||
mcron-error
|
||||
show-version
|
||||
show-package-information
|
||||
process-update-request
|
||||
get-user)
|
||||
#:re-export (read-string))
|
||||
|
||||
(define (mcron-error exit-code . rest)
|
||||
"Print an error message (made up from the parts of REST), and if the
|
||||
EXIT-CODE error is fatal (present and non-zero) then exit to the system with
|
||||
EXIT-CODE."
|
||||
(with-output-to-port (current-error-port)
|
||||
(lambda ()
|
||||
(for-each display (cons "mcron: " rest))
|
||||
(newline)))
|
||||
(when (and exit-code (not (eq? exit-code 0)))
|
||||
(primitive-exit exit-code)))
|
||||
|
||||
(define-syntax-rule (catch-mcron-error exp ...)
|
||||
"Evaluate EXP .... if an 'mcron-error exception occurs, print its diagnostics
|
||||
and exit with its error code."
|
||||
(catch 'mcron-error
|
||||
(lambda () exp ...)
|
||||
(lambda (key exit-code . msg)
|
||||
(apply mcron-error exit-code msg))))
|
||||
|
||||
(define (show-version command)
|
||||
"Display version information for COMMAND and quit."
|
||||
(let* ((name config-package-name)
|
||||
(short-name (cadr (string-split name #\space)))
|
||||
(version config-package-version))
|
||||
(simple-format #t "~a (~a) ~a
|
||||
Copyright (C) 2020 the ~a authors.
|
||||
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law.\n"
|
||||
command name version short-name)))
|
||||
|
||||
(define (show-package-information)
|
||||
"Display where to get help and send bug reports."
|
||||
(simple-format #t "\nReport bugs to: ~a.
|
||||
~a home page: <~a>
|
||||
General help using GNU software: <http://www.gnu.org/gethelp/>\n"
|
||||
config-package-bugreport
|
||||
config-package-name
|
||||
config-package-url))
|
||||
|
||||
(define (process-update-request fdes-list)
|
||||
"Read a user name from the socket, dealing with the /etc/crontab special
|
||||
case, remove all the user's jobs from the job list, and then re-read the
|
||||
user's updated file. In the special case drop all the system jobs and re-read
|
||||
the /etc/crontab file. This function should be called whenever a message
|
||||
comes in on the above socket."
|
||||
(let* ((sock (car (accept (car fdes-list))))
|
||||
(user-name (read-line sock)))
|
||||
(close sock)
|
||||
(set-configuration-time (current-time))
|
||||
(catch-mcron-error
|
||||
(if (string=? user-name "/etc/crontab")
|
||||
(begin
|
||||
(clear-system-jobs)
|
||||
(use-system-job-list)
|
||||
(read-vixie-file "/etc/crontab" parse-system-vixie-line)
|
||||
(use-user-job-list))
|
||||
(let ((user (getpw user-name)))
|
||||
(remove-user-jobs user)
|
||||
(set-configuration-user user)
|
||||
(read-vixie-file (string-append config-spool-dir "/" user-name)))))))
|
||||
|
||||
(define (get-user spec)
|
||||
"Return the passwd entry corresponding to SPEC. If SPEC is passwd entry
|
||||
then return it. If SPEC is not a valid specification throw an exception."
|
||||
(cond ((or (string? spec) (integer? spec))
|
||||
(getpw spec))
|
||||
((vector? spec) ;assume a user passwd entry
|
||||
spec)
|
||||
(else
|
||||
(throw 'invalid-user-specification spec))))
|
||||
|
|
@ -1,205 +0,0 @@
|
|||
;;;; vixie-specification.scm -- read Vixie-sytle configuration file
|
||||
;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
;;;; Commentary:
|
||||
;;;
|
||||
;;; Methods for reading a complete Vixie-style configuration file, either from
|
||||
;;; a real file or an already opened port. It also exposes the method for
|
||||
;;; parsing the time-specification part of a Vixie string, so that these can
|
||||
;;; be used to form the next-time-function of a job in a Guile configuration
|
||||
;;; file.
|
||||
;;;
|
||||
;;;; Code:
|
||||
|
||||
(define-module (mcron vixie-specification)
|
||||
#:use-module (ice-9 regex)
|
||||
#:use-module (ice-9 rdelim)
|
||||
#:use-module (mcron base)
|
||||
#:use-module (mcron config)
|
||||
#:use-module (mcron job-specifier)
|
||||
#:use-module (mcron redirect)
|
||||
#:use-module (mcron vixie-time)
|
||||
#:use-module (srfi srfi-1)
|
||||
#:export (parse-user-vixie-line
|
||||
parse-system-vixie-line
|
||||
read-vixie-port
|
||||
read-vixie-file
|
||||
check-system-crontab))
|
||||
|
||||
;; A line in a Vixie-style crontab file which gives a command specification
|
||||
;; carries two pieces of information: a time specification consisting of five
|
||||
;; space-separated items, and a command which is also separated from the time
|
||||
;; specification by a space. The line is broken into the two components, and the
|
||||
;; job procedure run to add the two pieces of information to the job list (this
|
||||
;; will in turn use the above function to turn the time specification into a
|
||||
;; function for computing future run times of the command).
|
||||
|
||||
(define parse-user-vixie-line-regexp
|
||||
(make-regexp "^[[:space:]]*(([^[:space:]]+[[:space:]]+){5})(.*)$"))
|
||||
|
||||
(define (parse-user-vixie-line line)
|
||||
(let ((match (regexp-exec parse-user-vixie-line-regexp line)))
|
||||
(if (not match)
|
||||
(throw 'mcron-error 10 "Bad job line in Vixie file."))
|
||||
(job (match:substring match 1)
|
||||
(lambda () (with-mail-out (match:substring match 3)))
|
||||
(match:substring match 3))))
|
||||
|
||||
|
||||
|
||||
;; The case of reading a line from /etc/crontab is similar to above but the user
|
||||
;; ID appears in the sixth field, before the action.
|
||||
|
||||
(define parse-system-vixie-line-regexp
|
||||
(make-regexp (string-append "^[[:space:]]*(([^[:space:]]+[[:space:]]+){5})"
|
||||
"([[:alpha:]][[:alnum:]_]*)[[:space:]]+(.*)$")))
|
||||
|
||||
(define (parse-system-vixie-line line)
|
||||
(let ((match (regexp-exec parse-system-vixie-line-regexp line)))
|
||||
(if (not match)
|
||||
(throw 'mcron-error 11 "Bad job line in /etc/crontab."))
|
||||
(let ((user (match:substring match 3)))
|
||||
(set-configuration-user user)
|
||||
(job (match:substring match 1)
|
||||
(lambda () (with-mail-out (match:substring match 4)
|
||||
user))
|
||||
(match:substring match 4)))))
|
||||
|
||||
|
||||
|
||||
;; Procedure to act on an environment variable specification in a Vixie-style
|
||||
;; configuration file, by adding an entry to the alist above. Returns #t if the
|
||||
;; operation was successful, #f if the line could not be interpreted as an
|
||||
;; environment specification.
|
||||
|
||||
(define parse-vixie-environment-regexp1
|
||||
(make-regexp
|
||||
"^[ \t]*([[:alpha:]_][[:alnum:]_]*)[ \t]*=[ \t]*\"(.*)\"[ \t]*$"))
|
||||
(define parse-vixie-environment-regexp2
|
||||
(make-regexp
|
||||
"^[ \t]*([[:alpha:]_][[:alnum:]_]*)[ \t]*=[ \t]*'(.*)'[ \t]*$"))
|
||||
(define parse-vixie-environment-regexp3
|
||||
(make-regexp
|
||||
"^[ \t]*([[:alpha:]_][[:alnum:]_]*)[ \t]*=[ \t]*(.*[^ \t])[ \t]*$"))
|
||||
(define parse-vixie-environment-regexp4
|
||||
(make-regexp
|
||||
"^[ \t]*([[:alpha:]_][[:alnum:]_]*)[ \t]*=[ \t]*$"))
|
||||
|
||||
|
||||
(define (parse-vixie-environment string)
|
||||
(let ((match (or (regexp-exec parse-vixie-environment-regexp1 string)
|
||||
(regexp-exec parse-vixie-environment-regexp2 string)
|
||||
(regexp-exec parse-vixie-environment-regexp3 string))))
|
||||
(if match
|
||||
(append-environment-mods (match:substring match 1)
|
||||
(match:substring match 2))
|
||||
(and=> (regexp-exec parse-vixie-environment-regexp4 string)
|
||||
(λ (match)
|
||||
(append-environment-mods (match:substring match 1) #f))))))
|
||||
|
||||
;; The next procedure reads an entire Vixie-style file. For each line in the
|
||||
;; file there are three possibilities (after continuation lines have been
|
||||
;; appended): the line is blank or contains only a comment, the line contains an
|
||||
;; environment modifier which will be handled in the mcron environment module,
|
||||
;; or the line contains a command specification in which case we use the
|
||||
;; procedure above to add an entry to the internal job list.
|
||||
;;
|
||||
;; Note that the environment modifications are cleared, so that there is no
|
||||
;; interference between crontab files (this might lead to unpredictable
|
||||
;; behaviour because the order in which crontab files are processed, if there is
|
||||
;; more than one, is generally undefined).
|
||||
|
||||
(define read-vixie-file-comment-regexp
|
||||
(make-regexp "^[[:space:]]*(#.*)?$"))
|
||||
|
||||
|
||||
(define (read-vixie-port port . parse-vixie-line)
|
||||
(clear-environment-mods)
|
||||
(if port
|
||||
(let ((parse-vixie-line
|
||||
(if (null? parse-vixie-line) parse-user-vixie-line
|
||||
(car parse-vixie-line))))
|
||||
(do ((line (read-line port) (read-line port))
|
||||
(line-number 1 (1+ line-number)))
|
||||
((eof-object? line))
|
||||
|
||||
(let ((report-line line-number))
|
||||
;; If the line ends with \, append the next line.
|
||||
(while (and (>= (string-length line) 1)
|
||||
(char=? (string-ref line
|
||||
(- (string-length line) 1))
|
||||
#\\))
|
||||
(let ((next-line (read-line port)))
|
||||
(if (eof-object? next-line)
|
||||
(set! next-line ""))
|
||||
(set! line-number (1+ line-number))
|
||||
(set! line
|
||||
(string-append
|
||||
(substring line 0 (- (string-length line) 1))
|
||||
next-line))))
|
||||
|
||||
(catch 'mcron-error
|
||||
(lambda ()
|
||||
;; Consider the three cases mentioned in the description.
|
||||
(or (regexp-exec read-vixie-file-comment-regexp line)
|
||||
(parse-vixie-environment line)
|
||||
(parse-vixie-line line)))
|
||||
(lambda (key exit-code . msg)
|
||||
(throw 'mcron-error exit-code
|
||||
(apply string-append
|
||||
(number->string report-line)
|
||||
": "
|
||||
msg)))))))))
|
||||
|
||||
|
||||
|
||||
;; If a file cannot be opened, we must silently ignore it because it may have
|
||||
;; been removed by crontab. However, if the file is there it must be parseable,
|
||||
;; otherwise the error must be propagated to the caller.
|
||||
|
||||
(define (read-vixie-file file-path . parse-vixie-line)
|
||||
(let ((port #f))
|
||||
(catch #t (lambda () (set! port (open-input-file file-path)))
|
||||
(lambda (key . args) (set! port #f)))
|
||||
(if port
|
||||
(catch 'mcron-error
|
||||
(lambda ()
|
||||
(if (null? parse-vixie-line)
|
||||
(read-vixie-port port)
|
||||
(read-vixie-port port (car parse-vixie-line)))
|
||||
(close port))
|
||||
(lambda (key exit-code . msg)
|
||||
(close port)
|
||||
(throw 'mcron-error exit-code
|
||||
(apply string-append file-path ":" msg)))))))
|
||||
|
||||
|
||||
;; A procedure which determines if the /etc/crontab file has been recently
|
||||
;; modified, and, if so, signals the main routine to re-read the file. We run
|
||||
;; under the with-mail-to command so that the process runs as a child,
|
||||
;; preventing lockup. If cron is supposed to check for updates to /etc/crontab,
|
||||
;; then this procedure will be called about 5 seconds before every minute.
|
||||
|
||||
(define (check-system-crontab)
|
||||
(with-mail-out (lambda ()
|
||||
(let ((mtime (stat:mtime (stat "/etc/crontab"))))
|
||||
(if (> mtime (- (current-time) 60))
|
||||
(let ((socket (socket AF_UNIX SOCK_STREAM 0)))
|
||||
(connect socket AF_UNIX config-socket-file)
|
||||
(display "/etc/crontab" socket)
|
||||
(close socket)))))))
|
||||
|
|
@ -1,374 +0,0 @@
|
|||
;;;; vixie-time.scm -- parse Vixie-style times
|
||||
;;; Copyright © 2003 Dale Mellor <dale_mellor@users.sourceforge.net>
|
||||
;;; Copyright © 2018, 2020 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(define-module (mcron vixie-time)
|
||||
#:use-module (ice-9 match)
|
||||
#:use-module (ice-9 regex)
|
||||
#:use-module (mcron job-specifier)
|
||||
#:use-module (srfi srfi-1)
|
||||
#:export (parse-vixie-time))
|
||||
|
||||
;; In Vixie-style time specifications three-letter symbols are allowed to stand
|
||||
;; for the numbers corresponding to months and days of the week. We deal with
|
||||
;; this by making a textual substitution early on in the processing of the
|
||||
;; strings.
|
||||
;;
|
||||
;; We start by defining, once and for all, a list of cons cells consisting of
|
||||
;; regexps which will match the symbols - which allow an arbitrary number of
|
||||
;; other letters to appear after them (so that the user can optionally complete
|
||||
;; the month and day names; this is an extension of Vixie) - and the value which
|
||||
;; is to replace the symbol.
|
||||
;;
|
||||
;; The procedure then takes a string, and then for each symbol in the
|
||||
;; parse-symbols list attempts to locate an instance and replace it with an
|
||||
;; ASCII representation of the value it stands for. The procedure returns the
|
||||
;; modified string. (Note that each symbol can appear only once, which meets the
|
||||
;; Vixie specifications technically but still allows silly users to mess things
|
||||
;; up).
|
||||
|
||||
(define parse-symbols
|
||||
(map (lambda (symbol-cell)
|
||||
(cons (make-regexp (string-append (car symbol-cell) "[[:alpha:]]*")
|
||||
regexp/icase)
|
||||
(cdr symbol-cell)))
|
||||
'(("jan" . "0") ("feb" . "1") ("mar" . "2") ("apr" . "3")
|
||||
("may" . "4") ("jun" . "5") ("jul" . "6") ("aug" . "7")
|
||||
("sep" . "8") ("oct" . "9") ("nov" . "10") ("dec" . "11")
|
||||
|
||||
("sun" . "0") ("mon" . "1") ("tue" . "2") ("wed" . "3")
|
||||
("thu" . "4") ("fri" . "5") ("sat" . "6") )))
|
||||
|
||||
(define (vixie-substitute-parse-symbols string)
|
||||
(for-each (lambda (symbol-cell)
|
||||
(let ((match (regexp-exec (car symbol-cell) string)))
|
||||
(if match
|
||||
(set! string (string-append (match:prefix match)
|
||||
(cdr symbol-cell)
|
||||
(match:suffix match))))))
|
||||
parse-symbols)
|
||||
string)
|
||||
|
||||
|
||||
|
||||
;; A Vixie time specification is made up of a space-separated list of elements,
|
||||
;; and the elements consist of a comma-separated list of subelements. The
|
||||
;; procedure below takes a string holding a subelement, which should have no
|
||||
;; spaces or symbols (see above) in it, and returns a list of all values which
|
||||
;; that subelement indicates. There are five distinct cases which must be dealt
|
||||
;; with: [1] a single '*' which returns a list of all values; [2] a '*' followed
|
||||
;; by a step specifier; [3] a range and step specifier; [4] a range; and [5] a
|
||||
;; single number.
|
||||
;;
|
||||
;; To perform the computation required for the '*' cases, we need to pass the
|
||||
;; limit of the allowable range for this subelement as the third argument. As
|
||||
;; days of the month start at 1 while all the other time components start at 0,
|
||||
;; we must pass the base of the range to deal with this case also.
|
||||
|
||||
(define parse-vixie-subelement-regexp
|
||||
(make-regexp "^([[:digit:]]+)(-([[:digit:]]+)(/([[:digit:]]+))?)?$"))
|
||||
|
||||
(define (parse-vixie-subelement string base limit)
|
||||
(if (char=? (string-ref string 0) #\*)
|
||||
(range base limit (if (> (string-length string) 1)
|
||||
(string->number (substring string 2)) ;; [2]
|
||||
1)) ;; [1]
|
||||
(let ((match (regexp-exec parse-vixie-subelement-regexp string)))
|
||||
(cond ((not match)
|
||||
(throw 'mcron-error 9
|
||||
"Bad Vixie-style time specification."))
|
||||
((match:substring match 5)
|
||||
(range (string->number (match:substring match 1))
|
||||
(+ 1 (string->number (match:substring match 3)))
|
||||
(string->number (match:substring match 5)))) ;; [3]
|
||||
((match:substring match 3)
|
||||
(range (string->number (match:substring match 1))
|
||||
(+ 1 (string->number (match:substring match 3))))) ;; [4]
|
||||
(else
|
||||
(list (string->number (match:substring match 1)))))))) ;; [5]
|
||||
|
||||
|
||||
|
||||
;; A Vixie element contains the entire specification, without spaces or symbols,
|
||||
;; of the acceptable values for one of the time components (minutes, hours,
|
||||
;; days, months, week days). Here we break the comma-separated list into
|
||||
;; subelements, and process each with the procedure above. The return value is a
|
||||
;; list of all the valid values of all the subcomponents.
|
||||
;;
|
||||
;; The second and third arguments are the base and upper limit on the values
|
||||
;; that can be accepted for this time element.
|
||||
;;
|
||||
;; The effect of the 'apply append' is to merge a list of lists into a single
|
||||
;; list.
|
||||
|
||||
(define (parse-vixie-element string base limit)
|
||||
(apply append
|
||||
(map (lambda (sub-element)
|
||||
(parse-vixie-subelement sub-element base limit))
|
||||
(string-tokenize string (char-set-complement (char-set #\,))))))
|
||||
|
||||
(define (interpolate-weekdays mday-list wday-list month year)
|
||||
"Given a list of days in the month MDAY-LIST and a list of days in the week
|
||||
WDAY-LIST, return an augmented list of days in the month with weekdays
|
||||
accounted for."
|
||||
(let ((t (localtime 0)))
|
||||
(set-tm:mday t 1)
|
||||
(set-tm:mon t month)
|
||||
(set-tm:year t year)
|
||||
(let ((first-day (tm:wday (cdr (mktime t)))))
|
||||
(define (range-wday wday)
|
||||
(let* ((first (- wday first-day))
|
||||
(first* (if (negative? first) (+ 7 first) first)))
|
||||
(range (1+ first*) 32 7)))
|
||||
(apply append mday-list (map range-wday wday-list)))))
|
||||
|
||||
;; Return the number of days in a month. Fix up a tm object for the zero'th day
|
||||
;; of the next month, rationalize the object and extract the day.
|
||||
|
||||
(define (days-in-month month year)
|
||||
(let ((t (localtime 0))) (set-tm:mday t 0)
|
||||
(set-tm:mon t (+ month 1))
|
||||
(set-tm:year t year)
|
||||
(tm:mday (cdr (mktime t)))))
|
||||
|
||||
|
||||
|
||||
;; We will be working with a list of time-spec's, one for each element of a time
|
||||
;; specification (minute, hour, ...). Each time-spec holds three pieces of
|
||||
;; information: a list of acceptable values for this time component, a procedure
|
||||
;; to get the component from a tm object, and a procedure to set the component
|
||||
;; in a tm object.
|
||||
|
||||
(define (time-spec:list time-spec) (vector-ref time-spec 0))
|
||||
(define (time-spec:getter time-spec) (vector-ref time-spec 1))
|
||||
(define (time-spec:setter time-spec) (vector-ref time-spec 2))
|
||||
|
||||
|
||||
|
||||
;; This procedure modifies the time tm object by setting the component referred
|
||||
;; to by the time-spec object to its next acceptable value. If this value is not
|
||||
;; greater than the original (because we have wrapped around the top of the
|
||||
;; acceptable values list), then the function returns #t, otherwise it returns
|
||||
;; #f. Thus, if the return value is true then it will be necessary for the
|
||||
;; caller to increment the next coarser time component as well.
|
||||
;;
|
||||
;; The first part of the let block is a concession to humanity; the procedure is
|
||||
;; simply unreadable without all of these aliases.
|
||||
|
||||
(define (increment-time-component time time-spec)
|
||||
(let ((time-list (time-spec:list time-spec))
|
||||
(getter (time-spec:getter time-spec))
|
||||
(setter (time-spec:setter time-spec))
|
||||
(find-best-next (@@ (mcron job-specifier) %find-best-next)))
|
||||
(match (find-best-next (getter time) time-list)
|
||||
((smallest . closest+)
|
||||
(let ((infinite (inf? closest+)))
|
||||
(if infinite
|
||||
(setter time smallest)
|
||||
(setter time closest+))
|
||||
infinite)))))
|
||||
|
||||
;; There now follows a set of procedures for adjusting an element of time,
|
||||
;; i.e. taking it to the next acceptable value. In each case, the head of the
|
||||
;; time-spec-list is expected to correspond to the component of time in
|
||||
;; question. If the adjusted value wraps around its allowed range, then the next
|
||||
;; biggest element of time must be adjusted, and so on.
|
||||
|
||||
;; There is no specification allowed for the year component of
|
||||
;; time. Therefore, if we have to make an adjustment (presumably because a
|
||||
;; monthly adjustment has wrapped around the top of its range) we can simply
|
||||
;; go to the next year.
|
||||
|
||||
(define (nudge-year! time)
|
||||
(set-tm:year time (+ (tm:year time) 1)))
|
||||
|
||||
|
||||
;; We nudge the month by finding the next allowable value, and if it wraps
|
||||
;; around we also nudge the year. The time-spec-list will have time-spec
|
||||
;; objects for month and weekday.
|
||||
|
||||
(define (nudge-month! time time-spec-list)
|
||||
(and (increment-time-component time (car time-spec-list))
|
||||
(nudge-year! time)))
|
||||
|
||||
|
||||
;; Try to increment the day component of the time according to the combination
|
||||
;; of the mday-list and the wday-list. If this wraps around the range, or if
|
||||
;; this falls outside the current month (31st February, for example), then
|
||||
;; bump the month, set the day to zero, and recurse on this procedure to find
|
||||
;; the next day in the new month.
|
||||
;;
|
||||
;; The time-spec-list will have time-spec entries for mday, month, and
|
||||
;; weekday.
|
||||
|
||||
(define (nudge-day! time time-spec-list)
|
||||
(if (or (increment-time-component
|
||||
time
|
||||
(vector
|
||||
(interpolate-weekdays (time-spec:list (car time-spec-list))
|
||||
(time-spec:list (caddr time-spec-list))
|
||||
(tm:mon time)
|
||||
(tm:year time))
|
||||
tm:mday
|
||||
set-tm:mday))
|
||||
(> (tm:mday time) (days-in-month (tm:mon time) (tm:year time))))
|
||||
(begin
|
||||
(nudge-month! time (cdr time-spec-list))
|
||||
(set-tm:mday time 0)
|
||||
(nudge-day! time time-spec-list))))
|
||||
|
||||
|
||||
|
||||
;; The hour is bumped to the next accceptable value, and the day is bumped if
|
||||
;; the hour wraps around.
|
||||
;;
|
||||
;; The time-spec-list holds specifications for hour, mday, month and weekday.
|
||||
|
||||
(define (nudge-hour! time time-spec-list)
|
||||
(and (increment-time-component time (car time-spec-list))
|
||||
(nudge-day! time (cdr time-spec-list))))
|
||||
|
||||
|
||||
|
||||
;; The minute is bumped to the next accceptable value, and the hour is bumped
|
||||
;; if the minute wraps around.
|
||||
;;
|
||||
;; The time-spec-list holds specifications for minute, hour, day-date, month
|
||||
;; and weekday.
|
||||
|
||||
(define (nudge-min! time time-spec-list)
|
||||
(and (increment-time-component time (car time-spec-list))
|
||||
(nudge-hour! time (cdr time-spec-list))))
|
||||
|
||||
|
||||
|
||||
|
||||
;; This is a procedure which returns a procedure which computes the next time a
|
||||
;; command should run after the current time, based on the information in the
|
||||
;; Vixie-style time specification.
|
||||
;;
|
||||
;; We start by computing a list of time-spec objects (described above) for the
|
||||
;; minute, hour, date, month, year and weekday components of the overall time
|
||||
;; specification [1]. Special care is taken to produce proper values for
|
||||
;; fields 2 and 4: according to Vixie specification "If both fields are
|
||||
;; restricted (ie, aren't *), the command will be run when _either_ field
|
||||
;; matches the current time." This implies that if one of these fields is *,
|
||||
;; while the other is not, its value should be '() [0], otherwise
|
||||
;; interpolate-weekdays below will produce incorrect results.
|
||||
|
||||
;; When we create the return procedure, it is this list to
|
||||
;; which references to a time-spec-list will be bound. It will be used by the
|
||||
;; returned procedure [3] to compute the next time a function should run. Any
|
||||
;; 7's in the weekday component of the list (the last one) are folded into 0's
|
||||
;; (both values represent sunday) [2]. Any 0's in the month-day component of the
|
||||
;; list are removed (this allows a solitary zero to be used to indicate that
|
||||
;; jobs should only run on certain days of the _week_) [2.1].
|
||||
;;
|
||||
;; The returned procedure itself:-
|
||||
;;
|
||||
;; Starts by obtaining the current broken-down time [4], and fixing it to
|
||||
;; ensure that it is an acceptable value, as follows. Each component from the
|
||||
;; biggest down is checked for acceptability, and if it is not acceptable it
|
||||
;; is bumped to the next acceptable value (this may cause higher components to
|
||||
;; also be bumped if there is range wrap-around) and all the lower components
|
||||
;; are set to -1 so that it can successfully be bumped up to zero if this is
|
||||
;; an allowed value. The -1 value will be bumped up subsequently to an allowed
|
||||
;; value [5].
|
||||
;;
|
||||
;; Once it has been asserted that the current time is acceptable, or has been
|
||||
;; adjusted to one minute before the next acceptable time, the minute
|
||||
;; component is then bumped to the next acceptable time, which may ripple
|
||||
;; through the higher components if necessary [6]. We now have the next time
|
||||
;; the command needs to run.
|
||||
;;
|
||||
;; The new time is then converted back into a UNIX time and returned [7].
|
||||
|
||||
(define (parse-vixie-time string)
|
||||
(let ((tokens (string-tokenize (vixie-substitute-parse-symbols string))))
|
||||
(cond
|
||||
((> (length tokens) 5)
|
||||
(throw 'mcron-error 9
|
||||
"Too many fields in Vixie-style time specification"))
|
||||
((< (length tokens) 5)
|
||||
(throw 'mcron-error 9
|
||||
"Not enough fields in Vixie-style time specification")))
|
||||
(match (map-in-order
|
||||
(λ (x)
|
||||
(vector
|
||||
(let* ((n (vector-ref x 0))
|
||||
(tok (list-ref tokens n)))
|
||||
(cond
|
||||
((and (= n 4)
|
||||
(string=? tok "*")
|
||||
(not (string=?
|
||||
(list-ref tokens 2) "*")))
|
||||
'())
|
||||
((and (= n 2)
|
||||
(string=? tok "*")
|
||||
(not (string=?
|
||||
(list-ref tokens 4) "*")))
|
||||
'())
|
||||
(else
|
||||
(parse-vixie-element
|
||||
tok
|
||||
(vector-ref x 1)
|
||||
(vector-ref x 2))))) ; [0]
|
||||
(vector-ref x 3)
|
||||
(vector-ref x 4)))
|
||||
;; token range-top+1 getter setter
|
||||
`( #( 0 0 60 ,tm:min ,set-tm:min )
|
||||
#( 1 0 24 ,tm:hour ,set-tm:hour )
|
||||
#( 2 1 32 ,tm:mday ,set-tm:mday )
|
||||
#( 3 0 12 ,tm:mon ,set-tm:mon )
|
||||
#( 4 0 7 ,tm:wday ,set-tm:wday ))) ;; [1]
|
||||
((and time-spec-list (min hour day month wday))
|
||||
(vector-set! wday
|
||||
0
|
||||
(map (lambda (time-spec)
|
||||
(if (eqv? time-spec 7) 0 time-spec))
|
||||
(vector-ref wday 0))) ;; [2]
|
||||
|
||||
(vector-set! day
|
||||
0
|
||||
(remove (lambda (d) (eqv? d 0))
|
||||
(vector-ref day 0))) ;; [2.1]
|
||||
|
||||
(λ (current-time) ;; [3]
|
||||
(let ((time (localtime current-time))) ;; [4]
|
||||
(unless (member (tm:mon time) (time-spec:list month))
|
||||
(nudge-month! time (cdddr time-spec-list))
|
||||
(set-tm:mday time 0))
|
||||
(when (or (eqv? (tm:mday time) 0)
|
||||
(not (member (tm:mday time)
|
||||
(interpolate-weekdays
|
||||
(time-spec:list day)
|
||||
(time-spec:list wday)
|
||||
(tm:mon time)
|
||||
(tm:year time)))))
|
||||
(nudge-day! time (cddr time-spec-list))
|
||||
(set-tm:hour time -1))
|
||||
(unless (member (tm:hour time)
|
||||
(time-spec:list hour))
|
||||
(nudge-hour! time (cdr time-spec-list))
|
||||
(set-tm:min time -1)) ;; [5]
|
||||
|
||||
(set-tm:sec time 0)
|
||||
(nudge-min! time time-spec-list) ;; [6]
|
||||
(first (mktime time)))))))) ;; [7]
|
||||
|
||||
|
||||
215
tests/base.scm
215
tests/base.scm
|
|
@ -1,215 +0,0 @@
|
|||
;;;; base.scm -- tests for (mcron base) module
|
||||
;;; Copyright © 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(use-modules (srfi srfi-64)
|
||||
(srfi srfi-111)
|
||||
(mcron base))
|
||||
|
||||
(test-begin "base")
|
||||
|
||||
(setlocale LC_ALL "C")
|
||||
(setenv "TZ" "UTC0")
|
||||
|
||||
;;; Import private procedures.
|
||||
(define make-schedule (@@ (mcron base) make-schedule))
|
||||
(define schedule-current (@@ (mcron base) schedule-current))
|
||||
(define schedule-user (@@ (mcron base) schedule-user))
|
||||
(define schedule-system (@@ (mcron base) schedule-system))
|
||||
(define make-job (@@ (mcron base) make-job))
|
||||
(define find-next-jobs (@@ (mcron base) find-next-jobs))
|
||||
|
||||
(define %user0 #("user0" "x" 0 0 "user0" "/var/empty" "/bin/sh"))
|
||||
(define %user1 #("user1" "x" 1 1 "user1" "/var/empty" "/bin/sh"))
|
||||
|
||||
(define* (make-dummy-job #:optional (displayable "dummy")
|
||||
#:key
|
||||
(user (getpw))
|
||||
(time-proc 1+)
|
||||
(action (λ () "dummy action"))
|
||||
(environment '())
|
||||
(next-time 0))
|
||||
(make-job user time-proc action environment displayable next-time))
|
||||
|
||||
;;; Check 'use-system-job-list' and 'use-user-job-list' effect
|
||||
(let ((schdl (make-schedule '() '() 'user)))
|
||||
(use-system-job-list #:schedule schdl)
|
||||
(test-eq "use-system-job-list"
|
||||
'system
|
||||
(schedule-current schdl))
|
||||
|
||||
(use-user-job-list #:schedule schdl)
|
||||
(test-eq "use-user-job-list"
|
||||
'user
|
||||
(schedule-current schdl)))
|
||||
|
||||
;;; Check that 'remove-user-jobs' with only one type of user job clears the
|
||||
;;; schedule.
|
||||
(let* ((job (make-dummy-job #:user %user0))
|
||||
(schdl (make-schedule (list job) '() 'user)))
|
||||
(remove-user-jobs %user0 #:schedule schdl)
|
||||
(test-equal "remove-user-jobs: only one"
|
||||
'()
|
||||
(schedule-user schdl)))
|
||||
|
||||
;;; Check that 'remove-user-jobs' with only two types of user jobs keep the
|
||||
;;; other user jobs in the schedule.
|
||||
(let* ((job0 (make-dummy-job #:user %user0))
|
||||
(job1 (make-dummy-job #:user %user1))
|
||||
(schdl (make-schedule (list job0 job1) '() 'user)))
|
||||
(remove-user-jobs %user0 #:schedule schdl)
|
||||
(test-equal "remove-user-jobs: keep one"
|
||||
(list job1)
|
||||
(schedule-user schdl)))
|
||||
|
||||
;;; Check that 'clear-system-jobs' removes all system jobs and has no effect
|
||||
;;; on the user jobs.
|
||||
(let* ((job0 (make-dummy-job #:user %user0))
|
||||
(job1 (make-dummy-job #:user %user1))
|
||||
(schdl (make-schedule (list job0) (list job1) 'user)))
|
||||
(clear-system-jobs #:schedule schdl)
|
||||
(test-assert "clear-system-jobs: basic"
|
||||
(and (equal? (list job0) (schedule-user schdl))
|
||||
(equal? '() (schedule-system schdl)))))
|
||||
|
||||
;;; Check that 'add-job' adds a user job when the current schedule is 'user.
|
||||
(let ((schdl (make-schedule '() '() 'user)))
|
||||
(add-job 1+ (const #t) "job0" 0 "user" #:schedule schdl)
|
||||
(test-assert "add-job: user schedule"
|
||||
(and (= 1 (length (schedule-user schdl)))
|
||||
(= 0 (length (schedule-system schdl))))))
|
||||
|
||||
;;; Check that 'add-job' adds a system job when the current schedule is
|
||||
;;; 'system.
|
||||
(let ((schdl (make-schedule '() '() 'system)))
|
||||
(add-job 1+ (const #t) "job0" 0 "user" #:schedule schdl)
|
||||
(test-assert "add-job: system schedule"
|
||||
(and (= 0 (length (schedule-user schdl)))
|
||||
(= 1 (length (schedule-system schdl))))))
|
||||
|
||||
;;; Check that 'find-next-jobs' find the soonest job.
|
||||
(let* ((job0 (make-dummy-job #:user %user0 #:next-time 5))
|
||||
(job1 (make-dummy-job #:user %user1 #:next-time 10))
|
||||
(schdl (make-schedule (list job0) (list job1) 'user)))
|
||||
(test-equal "find-next-jobs: basic"
|
||||
(list 5 job0)
|
||||
(find-next-jobs #:schedule schdl)))
|
||||
|
||||
;;; Check that 'find-next-jobs' can find multiple soonest jobs.
|
||||
(let* ((job0 (make-dummy-job #:user %user0 #:next-time 5))
|
||||
(job1 (make-dummy-job #:user %user1 #:next-time 5))
|
||||
(schdl (make-schedule (list job0) (list job1) 'user)))
|
||||
(test-equal "find-next-jobs: two jobs"
|
||||
(list 5 job0 job1)
|
||||
(find-next-jobs #:schedule schdl)))
|
||||
|
||||
;;; Check that 'find-next-jobs' returns #f when the schedule is empty.
|
||||
(let ((schdl (make-schedule '() '() 'user)))
|
||||
(test-equal "find-next-jobs: empty"
|
||||
(list #f)
|
||||
(find-next-jobs #:schedule schdl)))
|
||||
|
||||
;;; Check output of 'display-schedule' with a basic schedule.
|
||||
(test-assert "display-schedule: basic"
|
||||
(and (equal?
|
||||
"Thu Jan 1 00:00:05 1970 +0000\ndummy\n\n"
|
||||
(let* ((job0 (make-dummy-job #:user %user0 #:next-time 5))
|
||||
(job1 (make-dummy-job #:user %user1 #:next-time 10))
|
||||
(schdl (make-schedule (list job0) (list job1) 'user)))
|
||||
(with-output-to-string
|
||||
(λ () (display-schedule 1 #:schedule schdl)))))
|
||||
(equal?
|
||||
(string-append
|
||||
"Thu Jan 1 00:00:05 1970 +0000\ndummy\n\n"
|
||||
"Thu Jan 1 00:00:06 1970 +0000\ndummy\n\n")
|
||||
(let* ((job0 (make-dummy-job #:user %user0 #:next-time 5))
|
||||
(job1 (make-dummy-job #:user %user1 #:next-time 10))
|
||||
(schdl (make-schedule (list job0) (list job1) 'user)))
|
||||
(with-output-to-string
|
||||
(λ () (display-schedule 2 #:schedule schdl)))))))
|
||||
|
||||
;;; Check output of 'display-schedule' with an empty schedule.
|
||||
(let ((schdl (make-schedule '() '() 'user)))
|
||||
(test-equal "display-schedule: empty"
|
||||
""
|
||||
(with-output-to-string
|
||||
(λ () (display-schedule 1 #:schedule schdl)))))
|
||||
|
||||
;;;
|
||||
;;; Running jobs
|
||||
;;;
|
||||
|
||||
;;; Import private global.
|
||||
(define number-children (@@ (mcron base) number-children))
|
||||
|
||||
;;; Import private procedures.
|
||||
(define update-number-children! (@@ (mcron base) update-number-children!))
|
||||
(define child-cleanup (@@ (mcron base) child-cleanup))
|
||||
(define run-job (@@ (mcron base) run-job))
|
||||
|
||||
;;; Check 'number-children' initial value.
|
||||
(test-equal "number-children: init"
|
||||
0
|
||||
(unbox number-children))
|
||||
|
||||
;;; Check 'update-number-children!' incrementation.
|
||||
(test-equal "update-number-children!: 1+"
|
||||
2
|
||||
(begin
|
||||
(update-number-children! 1+)
|
||||
(update-number-children! 1+)
|
||||
(unbox number-children)))
|
||||
|
||||
;;; Check 'update-number-children!' decrementation.
|
||||
(test-equal "update-number-children!: 1-"
|
||||
1
|
||||
(begin
|
||||
(update-number-children! 1-)
|
||||
(unbox number-children)))
|
||||
|
||||
;;; Check 'update-number-children!' constant value.
|
||||
(test-equal "update-number-children!: set value"
|
||||
0
|
||||
(begin
|
||||
(update-number-children! (const 0))
|
||||
(unbox number-children)))
|
||||
|
||||
;;; Check 'run-job' and 'child-cleanup'.
|
||||
;;; XXX: Having to use the filesystem for a unit test is wrong.
|
||||
(let* ((filename (tmpnam))
|
||||
(action (λ () (close-port (open-output-file filename))))
|
||||
(job (make-dummy-job #:user (getpw (getuid)) #:action action)))
|
||||
(dynamic-wind
|
||||
(const #t)
|
||||
(λ ()
|
||||
(sigaction SIGCHLD (const #t))
|
||||
(run-job job)
|
||||
;; Wait for the SIGCHLD signal sent when job exits.
|
||||
(pause)
|
||||
;; Check 'run-job' result and if the number of children is up-to-date.
|
||||
(test-equal "run-job: basic"
|
||||
1
|
||||
(and (access? filename F_OK)
|
||||
(unbox number-children)))
|
||||
(child-cleanup)
|
||||
;; Check that 'child-cleanup' updates the number of children.
|
||||
(test-equal "child-cleanup: one" 0 (unbox number-children)))
|
||||
(λ ()
|
||||
(and (access? filename F_OK) (delete-file filename))
|
||||
(sigaction SIGCHLD SIG_DFL))))
|
||||
|
||||
(test-end)
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
# basic.sh -- basic tests for mcron
|
||||
# Copyright © 2017 Mathieu Lirzin <mthl@gnu.org>
|
||||
#
|
||||
# This file is part of GNU Mcron.
|
||||
#
|
||||
# GNU Mcron is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# GNU Mcron is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
source "${srcdir}/tests/init.sh"
|
||||
|
||||
# Use current working directory to store mcron files
|
||||
XDG_CONFIG_HOME=`pwd`
|
||||
export XDG_CONFIG_HOME
|
||||
|
||||
mkdir cron
|
||||
cat > cron/foo.guile <<EOF
|
||||
(job '(next-second) '(display "foo\n"))
|
||||
EOF
|
||||
|
||||
mcron --schedule=1 cron/foo.guile > "output$$"
|
||||
grep -e "foo" "output$$" || fail_ "'foo.guile' job is not scheduled"
|
||||
|
||||
mcron --schedule=1 > "output$$"
|
||||
grep -e "foo" "output$$" || fail_ "'foo.guile' job is not scheduled"
|
||||
|
||||
Exit 0
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
;;;; environment.scm -- tests for (mcron environment) module
|
||||
;;; Copyright © 2016, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(use-modules (srfi srfi-64)
|
||||
(srfi srfi-111)
|
||||
(mcron environment))
|
||||
|
||||
(test-begin "environment")
|
||||
|
||||
;;; Check 'current-environment-mods' initial value which should be empty.
|
||||
(test-equal "current-environment-mods: init"
|
||||
'()
|
||||
(unbox (@@ (mcron environment) %current-environment-mods)))
|
||||
|
||||
;;; Check 'current-environment-mods-copy' with an empty environment
|
||||
(test-assert "current-environment-mods-copy: empty"
|
||||
(let* ((env (box '()))
|
||||
(copy0 (get-current-environment-mods-copy #:environ env))
|
||||
(copy1 (get-current-environment-mods-copy #:environ env)))
|
||||
(set! copy1 (assoc-set! copy1 "FOO" "BAR"))
|
||||
(and (equal? '() (unbox env))
|
||||
(equal? '() copy0)
|
||||
(equal? '(("FOO" . "BAR")) copy1))))
|
||||
|
||||
;;; Check 'current-environment-mods-copy' with a basic environment
|
||||
(test-assert "current-environment-mods-copy: basic"
|
||||
(let* ((init-env '(("a" . "1") ("b" . "2")))
|
||||
(env (box init-env))
|
||||
(copy0 (get-current-environment-mods-copy #:environ env))
|
||||
(copy1 (get-current-environment-mods-copy #:environ env)))
|
||||
(set! copy1 (assoc-set! copy1 "c" "3"))
|
||||
(and (equal? init-env (unbox env))
|
||||
(equal? init-env copy0)
|
||||
(equal? `(("c" . "3") . ,init-env) copy1))))
|
||||
|
||||
;;; Check 'append-environment-mods' basic call
|
||||
(test-equal "append-environment-mods: basic"
|
||||
"BAR"
|
||||
(let ((env (box '())))
|
||||
(append-environment-mods "FOO" "BAR" #:environ env)
|
||||
(assoc-ref (unbox env) "FOO")))
|
||||
|
||||
;;; Check 'append-environment-mods' that when adding the same key twice the
|
||||
;;; later is placed after the previous one.
|
||||
(test-equal "append-environment-mods: twice"
|
||||
'(("FOO" . "BAR") ("FOO" . "BAZ"))
|
||||
(let ((env (box '())))
|
||||
(append-environment-mods "FOO" "BAR" #:environ env)
|
||||
(append-environment-mods "FOO" "BAZ" #:environ env)
|
||||
(unbox env)))
|
||||
|
||||
;;; Check 'clear-environment-mods' side effect
|
||||
(test-equal "clear-environment-mods: effect"
|
||||
'()
|
||||
(let ((env (box '())))
|
||||
(append-environment-mods "FOO" "BAR" #:environ env)
|
||||
(append-environment-mods "FOO" "BAZ" #:environ env)
|
||||
(clear-environment-mods #:environ env)
|
||||
(unbox env)))
|
||||
|
||||
;;; Check 'modify-environment' basic call
|
||||
(test-assert "modifiy-environment: basic"
|
||||
(begin
|
||||
(modify-environment '(("FOO" . "bar")) (getpw))
|
||||
(equal? (getenv "FOO") "bar")))
|
||||
|
||||
(test-assert "modifiy-environment: user & logname"
|
||||
;; Check that USER and LOGNAME environment variables can't be changed.
|
||||
(let* ((user-entry (pk (getpw)))
|
||||
(user-name (passwd:name user-entry)))
|
||||
(modify-environment '(("USER" . "alice")) user-entry)
|
||||
(modify-environment '(("LOGNAME" . "bob")) user-entry)
|
||||
(equal? user-name
|
||||
(pk (getenv "USER"))
|
||||
(pk (getenv "LOGNAME")))))
|
||||
|
||||
(test-end)
|
||||
605
tests/init.sh
605
tests/init.sh
|
|
@ -1,605 +0,0 @@
|
|||
# source this file; set up for tests
|
||||
|
||||
# Copyright (C) 2009-2017 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Using this file in a test
|
||||
# =========================
|
||||
#
|
||||
# The typical skeleton of a test looks like this:
|
||||
#
|
||||
# #!/bin/sh
|
||||
# . "${srcdir=.}/init.sh"; path_prepend_ .
|
||||
# Execute some commands.
|
||||
# Note that these commands are executed in a subdirectory, therefore you
|
||||
# need to prepend "../" to relative filenames in the build directory.
|
||||
# Note that the "path_prepend_ ." is useful only if the body of your
|
||||
# test invokes programs residing in the initial directory.
|
||||
# For example, if the programs you want to test are in src/, and this test
|
||||
# script is named tests/test-1, then you would use "path_prepend_ ../src",
|
||||
# or perhaps export PATH='$(abs_top_builddir)/src$(PATH_SEPARATOR)'"$$PATH"
|
||||
# to all tests via automake's TESTS_ENVIRONMENT.
|
||||
# Set the exit code 0 for success, 77 for skipped, or 1 or other for failure.
|
||||
# Use the skip_ and fail_ functions to print a diagnostic and then exit
|
||||
# with the corresponding exit code.
|
||||
# Exit $?
|
||||
|
||||
# Executing a test that uses this file
|
||||
# ====================================
|
||||
#
|
||||
# Running a single test:
|
||||
# $ make check TESTS=test-foo.sh
|
||||
#
|
||||
# Running a single test, with verbose output:
|
||||
# $ make check TESTS=test-foo.sh VERBOSE=yes
|
||||
#
|
||||
# Running a single test, keeping the temporary directory:
|
||||
# $ make check TESTS=test-foo.sh KEEP=yes
|
||||
#
|
||||
# Running a single test, with single-stepping:
|
||||
# 1. Go into a sub-shell:
|
||||
# $ bash
|
||||
# 2. Set relevant environment variables from TESTS_ENVIRONMENT in the
|
||||
# Makefile:
|
||||
# $ export srcdir=../../tests # this is an example
|
||||
# 3. Execute the commands from the test, copy&pasting them one by one:
|
||||
# $ . "$srcdir/init.sh"; path_prepend_ .
|
||||
# ...
|
||||
# 4. Finally
|
||||
# $ exit
|
||||
|
||||
ME_=`expr "./$0" : '.*/\(.*\)$'`
|
||||
|
||||
# We use a trap below for cleanup. This requires us to go through
|
||||
# hoops to get the right exit status transported through the handler.
|
||||
# So use 'Exit STATUS' instead of 'exit STATUS' inside of the tests.
|
||||
# Turn off errexit here so that we don't trip the bug with OSF1/Tru64
|
||||
# sh inside this function.
|
||||
Exit () { set +e; (exit $1); exit $1; }
|
||||
|
||||
# Print warnings (e.g., about skipped and failed tests) to this file number.
|
||||
# Override by defining to say, 9, in init.cfg, and putting say,
|
||||
# export ...ENVVAR_SETTINGS...; $(SHELL) 9>&2
|
||||
# in the definition of TESTS_ENVIRONMENT in your tests/Makefile.am file.
|
||||
# This is useful when using automake's parallel tests mode, to print
|
||||
# the reason for skip/failure to console, rather than to the .log files.
|
||||
: ${stderr_fileno_=2}
|
||||
|
||||
# Note that correct expansion of "$*" depends on IFS starting with ' '.
|
||||
# Always write the full diagnostic to stderr.
|
||||
# When stderr_fileno_ is not 2, also emit the first line of the
|
||||
# diagnostic to that file descriptor.
|
||||
warn_ ()
|
||||
{
|
||||
# If IFS does not start with ' ', set it and emit the warning in a subshell.
|
||||
case $IFS in
|
||||
' '*) printf '%s\n' "$*" >&2
|
||||
test $stderr_fileno_ = 2 \
|
||||
|| { printf '%s\n' "$*" | sed 1q >&$stderr_fileno_ ; } ;;
|
||||
*) (IFS=' '; warn_ "$@");;
|
||||
esac
|
||||
}
|
||||
fail_ () { warn_ "$ME_: failed test: $@"; Exit 1; }
|
||||
skip_ () { warn_ "$ME_: skipped test: $@"; Exit 77; }
|
||||
fatal_ () { warn_ "$ME_: hard error: $@"; Exit 99; }
|
||||
framework_failure_ () { warn_ "$ME_: set-up failure: $@"; Exit 99; }
|
||||
|
||||
# This is used to simplify checking of the return value
|
||||
# which is useful when ensuring a command fails as desired.
|
||||
# I.e., just doing `command ... &&fail=1` will not catch
|
||||
# a segfault in command for example. With this helper you
|
||||
# instead check an explicit exit code like
|
||||
# returns_ 1 command ... || fail
|
||||
returns_ () {
|
||||
# Disable tracing so it doesn't interfere with stderr of the wrapped command
|
||||
{ set +x; } 2>/dev/null
|
||||
|
||||
local exp_exit="$1"
|
||||
shift
|
||||
"$@"
|
||||
test $? -eq $exp_exit && ret_=0 || ret_=1
|
||||
|
||||
if test "$VERBOSE" = yes && test "$gl_set_x_corrupts_stderr_" = false; then
|
||||
set -x
|
||||
fi
|
||||
{ return $ret_; } 2>/dev/null
|
||||
}
|
||||
|
||||
# Sanitize this shell to POSIX mode, if possible.
|
||||
DUALCASE=1; export DUALCASE
|
||||
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
|
||||
emulate sh
|
||||
NULLCMD=:
|
||||
alias -g '${1+"$@"}'='"$@"'
|
||||
setopt NO_GLOB_SUBST
|
||||
else
|
||||
case `(set -o) 2>/dev/null` in
|
||||
*posix*) set -o posix ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# We require $(...) support unconditionally.
|
||||
# We require non-surprising "local" semantics (this eliminates dash).
|
||||
# This takes the admittedly draconian step of eliminating dash, because the
|
||||
# assignment tab=$(printf '\t') works fine, yet preceding it with "local "
|
||||
# transforms it into an assignment that sets the variable to the empty string.
|
||||
# That is too counter-intuitive, and can lead to subtle run-time malfunction.
|
||||
# The example below is less subtle in that with dash, it evokes the run-time
|
||||
# exception "dash: 1: local: 1: bad variable name".
|
||||
# We require a few additional shell features only when $EXEEXT is nonempty,
|
||||
# in order to support automatic $EXEEXT emulation:
|
||||
# - hyphen-containing alias names
|
||||
# - we prefer to use ${var#...} substitution, rather than having
|
||||
# to work around lack of support for that feature.
|
||||
# The following code attempts to find a shell with support for these features.
|
||||
# If the current shell passes the test, we're done. Otherwise, test other
|
||||
# shells until we find one that passes. If one is found, re-exec it.
|
||||
# If no acceptable shell is found, skip the current test.
|
||||
#
|
||||
# The "...set -x; P=1 true 2>err..." test is to disqualify any shell that
|
||||
# emits "P=1" into err, as /bin/sh from SunOS 5.11 and OpenBSD 4.7 do.
|
||||
#
|
||||
# Use "9" to indicate success (rather than 0), in case some shell acts
|
||||
# like Solaris 10's /bin/sh but exits successfully instead of with status 2.
|
||||
|
||||
# Eval this code in a subshell to determine a shell's suitability.
|
||||
# 10 - passes all tests; ok to use
|
||||
# 9 - ok, but enabling "set -x" corrupts app stderr; prefer higher score
|
||||
# ? - not ok
|
||||
gl_shell_test_script_='
|
||||
test $(echo y) = y || exit 1
|
||||
f_local_() { local v=1; }; f_local_ || exit 1
|
||||
f_dash_local_fail_() { local t=$(printf " 1"); }; f_dash_local_fail_
|
||||
score_=10
|
||||
if test "$VERBOSE" = yes; then
|
||||
test -n "$( (exec 3>&1; set -x; P=1 true 2>&3) 2> /dev/null)" && score_=9
|
||||
fi
|
||||
test -z "$EXEEXT" && exit $score_
|
||||
shopt -s expand_aliases
|
||||
alias a-b="echo zoo"
|
||||
v=abx
|
||||
test ${v%x} = ab \
|
||||
&& test ${v#a} = bx \
|
||||
&& test $(a-b) = zoo \
|
||||
&& exit $score_
|
||||
'
|
||||
|
||||
if test "x$1" = "x--no-reexec"; then
|
||||
shift
|
||||
else
|
||||
# Assume a working shell. Export to subshells (setup_ needs this).
|
||||
gl_set_x_corrupts_stderr_=false
|
||||
export gl_set_x_corrupts_stderr_
|
||||
|
||||
# Record the first marginally acceptable shell.
|
||||
marginal_=
|
||||
|
||||
# Search for a shell that meets our requirements.
|
||||
for re_shell_ in __current__ "${CONFIG_SHELL:-no_shell}" \
|
||||
/bin/sh bash dash zsh pdksh fail
|
||||
do
|
||||
test "$re_shell_" = no_shell && continue
|
||||
|
||||
# If we've made it all the way to the sentinel, "fail" without
|
||||
# finding even a marginal shell, skip this test.
|
||||
if test "$re_shell_" = fail; then
|
||||
test -z "$marginal_" && skip_ failed to find an adequate shell
|
||||
re_shell_=$marginal_
|
||||
break
|
||||
fi
|
||||
|
||||
# When testing the current shell, simply "eval" the test code.
|
||||
# Otherwise, run it via $re_shell_ -c ...
|
||||
if test "$re_shell_" = __current__; then
|
||||
# 'eval'ing this code makes Solaris 10's /bin/sh exit with
|
||||
# $? set to 2. It does not evaluate any of the code after the
|
||||
# "unexpected" first '('. Thus, we must run it in a subshell.
|
||||
( eval "$gl_shell_test_script_" ) > /dev/null 2>&1
|
||||
else
|
||||
"$re_shell_" -c "$gl_shell_test_script_" 2>/dev/null
|
||||
fi
|
||||
|
||||
st_=$?
|
||||
|
||||
# $re_shell_ works just fine. Use it.
|
||||
if test $st_ = 10; then
|
||||
gl_set_x_corrupts_stderr_=false
|
||||
break
|
||||
fi
|
||||
|
||||
# If this is our first marginally acceptable shell, remember it.
|
||||
if test "$st_:$marginal_" = 9: ; then
|
||||
marginal_="$re_shell_"
|
||||
gl_set_x_corrupts_stderr_=true
|
||||
fi
|
||||
done
|
||||
|
||||
if test "$re_shell_" != __current__; then
|
||||
# Found a usable shell. Preserve -v and -x.
|
||||
case $- in
|
||||
*v*x* | *x*v*) opts_=-vx ;;
|
||||
*v*) opts_=-v ;;
|
||||
*x*) opts_=-x ;;
|
||||
*) opts_= ;;
|
||||
esac
|
||||
re_shell=$re_shell_
|
||||
export re_shell
|
||||
exec "$re_shell_" $opts_ "$0" --no-reexec "$@"
|
||||
echo "$ME_: exec failed" 1>&2
|
||||
exit 127
|
||||
fi
|
||||
fi
|
||||
|
||||
# If this is bash, turn off all aliases.
|
||||
test -n "$BASH_VERSION" && unalias -a
|
||||
|
||||
# Note that when supporting $EXEEXT (transparently mapping from PROG_NAME to
|
||||
# PROG_NAME.exe), we want to support hyphen-containing names like test-acos.
|
||||
# That is part of the shell-selection test above. Why use aliases rather
|
||||
# than functions? Because support for hyphen-containing aliases is more
|
||||
# widespread than that for hyphen-containing function names.
|
||||
test -n "$EXEEXT" && shopt -s expand_aliases
|
||||
|
||||
# Enable glibc's malloc-perturbing option.
|
||||
# This is useful for exposing code that depends on the fact that
|
||||
# malloc-related functions often return memory that is mostly zeroed.
|
||||
# If you have the time and cycles, use valgrind to do an even better job.
|
||||
: ${MALLOC_PERTURB_=87}
|
||||
export MALLOC_PERTURB_
|
||||
|
||||
# This is a stub function that is run upon trap (upon regular exit and
|
||||
# interrupt). Override it with a per-test function, e.g., to unmount
|
||||
# a partition, or to undo any other global state changes.
|
||||
cleanup_ () { :; }
|
||||
|
||||
# Emit a header similar to that from diff -u; Print the simulated "diff"
|
||||
# command so that the order of arguments is clear. Don't bother with @@ lines.
|
||||
emit_diff_u_header_ ()
|
||||
{
|
||||
printf '%s\n' "diff -u $*" \
|
||||
"--- $1 1970-01-01" \
|
||||
"+++ $2 1970-01-01"
|
||||
}
|
||||
|
||||
# Arrange not to let diff or cmp operate on /dev/null,
|
||||
# since on some systems (at least OSF/1 5.1), that doesn't work.
|
||||
# When there are not two arguments, or no argument is /dev/null, return 2.
|
||||
# When one argument is /dev/null and the other is not empty,
|
||||
# cat the nonempty file to stderr and return 1.
|
||||
# Otherwise, return 0.
|
||||
compare_dev_null_ ()
|
||||
{
|
||||
test $# = 2 || return 2
|
||||
|
||||
if test "x$1" = x/dev/null; then
|
||||
test -s "$2" || return 0
|
||||
emit_diff_u_header_ "$@"; sed 's/^/+/' "$2"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if test "x$2" = x/dev/null; then
|
||||
test -s "$1" || return 0
|
||||
emit_diff_u_header_ "$@"; sed 's/^/-/' "$1"
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 2
|
||||
}
|
||||
|
||||
for diff_opt_ in -u -U3 -c '' no; do
|
||||
test "$diff_opt_" != no &&
|
||||
diff_out_=`exec 2>/dev/null; diff $diff_opt_ "$0" "$0" < /dev/null` &&
|
||||
break
|
||||
done
|
||||
if test "$diff_opt_" != no; then
|
||||
if test -z "$diff_out_"; then
|
||||
compare_ () { diff $diff_opt_ "$@"; }
|
||||
else
|
||||
compare_ ()
|
||||
{
|
||||
# If no differences were found, AIX and HP-UX 'diff' produce output
|
||||
# like "No differences encountered". Hide this output.
|
||||
diff $diff_opt_ "$@" > diff.out
|
||||
diff_status_=$?
|
||||
test $diff_status_ -eq 0 || cat diff.out || diff_status_=2
|
||||
rm -f diff.out || diff_status_=2
|
||||
return $diff_status_
|
||||
}
|
||||
fi
|
||||
elif cmp -s /dev/null /dev/null 2>/dev/null; then
|
||||
compare_ () { cmp -s "$@"; }
|
||||
else
|
||||
compare_ () { cmp "$@"; }
|
||||
fi
|
||||
|
||||
# Usage: compare EXPECTED ACTUAL
|
||||
#
|
||||
# Given compare_dev_null_'s preprocessing, defer to compare_ if 2 or more.
|
||||
# Otherwise, propagate $? to caller: any diffs have already been printed.
|
||||
compare ()
|
||||
{
|
||||
# This looks like it can be factored to use a simple "case $?"
|
||||
# after unchecked compare_dev_null_ invocation, but that would
|
||||
# fail in a "set -e" environment.
|
||||
if compare_dev_null_ "$@"; then
|
||||
return 0
|
||||
else
|
||||
case $? in
|
||||
1) return 1;;
|
||||
*) compare_ "$@";;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
# An arbitrary prefix to help distinguish test directories.
|
||||
testdir_prefix_ () { printf gt; }
|
||||
|
||||
# Run the user-overridable cleanup_ function, remove the temporary
|
||||
# directory and exit with the incoming value of $?.
|
||||
remove_tmp_ ()
|
||||
{
|
||||
__st=$?
|
||||
cleanup_
|
||||
if test "$KEEP" = yes; then
|
||||
echo "Not removing temporary directory $test_dir_"
|
||||
else
|
||||
# cd out of the directory we're about to remove
|
||||
cd "$initial_cwd_" || cd / || cd /tmp
|
||||
chmod -R u+rwx "$test_dir_"
|
||||
# If removal fails and exit status was to be 0, then change it to 1.
|
||||
rm -rf "$test_dir_" || { test $__st = 0 && __st=1; }
|
||||
fi
|
||||
exit $__st
|
||||
}
|
||||
|
||||
# Given a directory name, DIR, if every entry in it that matches *.exe
|
||||
# contains only the specified bytes (see the case stmt below), then print
|
||||
# a space-separated list of those names and return 0. Otherwise, don't
|
||||
# print anything and return 1. Naming constraints apply also to DIR.
|
||||
find_exe_basenames_ ()
|
||||
{
|
||||
feb_dir_=$1
|
||||
feb_fail_=0
|
||||
feb_result_=
|
||||
feb_sp_=
|
||||
for feb_file_ in $feb_dir_/*.exe; do
|
||||
# If there was no *.exe file, or there existed a file named "*.exe" that
|
||||
# was deleted between the above glob expansion and the existence test
|
||||
# below, just skip it.
|
||||
test "x$feb_file_" = "x$feb_dir_/*.exe" && test ! -f "$feb_file_" \
|
||||
&& continue
|
||||
# Exempt [.exe, since we can't create a function by that name, yet
|
||||
# we can't invoke [ by PATH search anyways due to shell builtins.
|
||||
test "x$feb_file_" = "x$feb_dir_/[.exe" && continue
|
||||
case $feb_file_ in
|
||||
*[!-a-zA-Z/0-9_.+]*) feb_fail_=1; break;;
|
||||
*) # Remove leading file name components as well as the .exe suffix.
|
||||
feb_file_=${feb_file_##*/}
|
||||
feb_file_=${feb_file_%.exe}
|
||||
feb_result_="$feb_result_$feb_sp_$feb_file_";;
|
||||
esac
|
||||
feb_sp_=' '
|
||||
done
|
||||
test $feb_fail_ = 0 && printf %s "$feb_result_"
|
||||
return $feb_fail_
|
||||
}
|
||||
|
||||
# Consider the files in directory, $1.
|
||||
# For each file name of the form PROG.exe, create an alias named
|
||||
# PROG that simply invokes PROG.exe, then return 0. If any selected
|
||||
# file name or the directory name, $1, contains an unexpected character,
|
||||
# define no alias and return 1.
|
||||
create_exe_shims_ ()
|
||||
{
|
||||
case $EXEEXT in
|
||||
'') return 0 ;;
|
||||
.exe) ;;
|
||||
*) echo "$0: unexpected \$EXEEXT value: $EXEEXT" 1>&2; return 1 ;;
|
||||
esac
|
||||
|
||||
base_names_=`find_exe_basenames_ $1` \
|
||||
|| { echo "$0 (exe_shim): skipping directory: $1" 1>&2; return 0; }
|
||||
|
||||
if test -n "$base_names_"; then
|
||||
for base_ in $base_names_; do
|
||||
alias "$base_"="$base_$EXEEXT"
|
||||
done
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
# Use this function to prepend to PATH an absolute name for each
|
||||
# specified, possibly-$initial_cwd_-relative, directory.
|
||||
path_prepend_ ()
|
||||
{
|
||||
while test $# != 0; do
|
||||
path_dir_=$1
|
||||
case $path_dir_ in
|
||||
'') fail_ "invalid path dir: '$1'";;
|
||||
/*) abs_path_dir_=$path_dir_;;
|
||||
*) abs_path_dir_=$initial_cwd_/$path_dir_;;
|
||||
esac
|
||||
case $abs_path_dir_ in
|
||||
*:*) fail_ "invalid path dir: '$abs_path_dir_'";;
|
||||
esac
|
||||
PATH="$abs_path_dir_:$PATH"
|
||||
|
||||
# Create an alias, FOO, for each FOO.exe in this directory.
|
||||
create_exe_shims_ "$abs_path_dir_" \
|
||||
|| fail_ "something failed (above): $abs_path_dir_"
|
||||
shift
|
||||
done
|
||||
export PATH
|
||||
}
|
||||
|
||||
setup_ ()
|
||||
{
|
||||
if test "$VERBOSE" = yes; then
|
||||
# Test whether set -x may cause the selected shell to corrupt an
|
||||
# application's stderr. Many do, including zsh-4.3.10 and the /bin/sh
|
||||
# from SunOS 5.11, OpenBSD 4.7 and Irix 5.x and 6.5.
|
||||
# If enabling verbose output this way would cause trouble, simply
|
||||
# issue a warning and refrain.
|
||||
if $gl_set_x_corrupts_stderr_; then
|
||||
warn_ "using SHELL=$SHELL with 'set -x' corrupts stderr"
|
||||
else
|
||||
set -x
|
||||
fi
|
||||
fi
|
||||
|
||||
initial_cwd_=$PWD
|
||||
|
||||
pfx_=`testdir_prefix_`
|
||||
test_dir_=`mktempd_ "$initial_cwd_" "$pfx_-$ME_.XXXX"` \
|
||||
|| fail_ "failed to create temporary directory in $initial_cwd_"
|
||||
cd "$test_dir_" || fail_ "failed to cd to temporary directory"
|
||||
|
||||
# As autoconf-generated configure scripts do, ensure that IFS
|
||||
# is defined initially, so that saving and restoring $IFS works.
|
||||
gl_init_sh_nl_='
|
||||
'
|
||||
IFS=" "" $gl_init_sh_nl_"
|
||||
|
||||
# This trap statement, along with a trap on 0 below, ensure that the
|
||||
# temporary directory, $test_dir_, is removed upon exit as well as
|
||||
# upon receipt of any of the listed signals.
|
||||
for sig_ in 1 2 3 13 15; do
|
||||
eval "trap 'Exit $(expr $sig_ + 128)' $sig_"
|
||||
done
|
||||
}
|
||||
|
||||
# Create a temporary directory, much like mktemp -d does.
|
||||
# Written by Jim Meyering.
|
||||
#
|
||||
# Usage: mktempd_ /tmp phoey.XXXXXXXXXX
|
||||
#
|
||||
# First, try to use the mktemp program.
|
||||
# Failing that, we'll roll our own mktemp-like function:
|
||||
# - try to get random bytes from /dev/urandom
|
||||
# - failing that, generate output from a combination of quickly-varying
|
||||
# sources and gzip. Ignore non-varying gzip header, and extract
|
||||
# "random" bits from there.
|
||||
# - given those bits, map to file-name bytes using tr, and try to create
|
||||
# the desired directory.
|
||||
# - make only $MAX_TRIES_ attempts
|
||||
|
||||
# Helper function. Print $N pseudo-random bytes from a-zA-Z0-9.
|
||||
rand_bytes_ ()
|
||||
{
|
||||
n_=$1
|
||||
|
||||
# Maybe try openssl rand -base64 $n_prime_|tr '+/=\012' abcd first?
|
||||
# But if they have openssl, they probably have mktemp, too.
|
||||
|
||||
chars_=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
|
||||
dev_rand_=/dev/urandom
|
||||
if test -r "$dev_rand_"; then
|
||||
# Note: 256-length($chars_) == 194; 3 copies of $chars_ is 186 + 8 = 194.
|
||||
dd ibs=$n_ count=1 if=$dev_rand_ 2>/dev/null \
|
||||
| LC_ALL=C tr -c $chars_ 01234567$chars_$chars_$chars_
|
||||
return
|
||||
fi
|
||||
|
||||
n_plus_50_=`expr $n_ + 50`
|
||||
cmds_='date; date +%N; free; who -a; w; ps auxww; ps ef; netstat -n'
|
||||
data_=` (eval "$cmds_") 2>&1 | gzip `
|
||||
|
||||
# Ensure that $data_ has length at least 50+$n_
|
||||
while :; do
|
||||
len_=`echo "$data_"|wc -c`
|
||||
test $n_plus_50_ -le $len_ && break;
|
||||
data_=` (echo "$data_"; eval "$cmds_") 2>&1 | gzip `
|
||||
done
|
||||
|
||||
echo "$data_" \
|
||||
| dd bs=1 skip=50 count=$n_ 2>/dev/null \
|
||||
| LC_ALL=C tr -c $chars_ 01234567$chars_$chars_$chars_
|
||||
}
|
||||
|
||||
mktempd_ ()
|
||||
{
|
||||
case $# in
|
||||
2);;
|
||||
*) fail_ "Usage: mktempd_ DIR TEMPLATE";;
|
||||
esac
|
||||
|
||||
destdir_=$1
|
||||
template_=$2
|
||||
|
||||
MAX_TRIES_=4
|
||||
|
||||
# Disallow any trailing slash on specified destdir:
|
||||
# it would subvert the post-mktemp "case"-based destdir test.
|
||||
case $destdir_ in
|
||||
/ | //) destdir_slash_=$destdir;;
|
||||
*/) fail_ "invalid destination dir: remove trailing slash(es)";;
|
||||
*) destdir_slash_=$destdir_/;;
|
||||
esac
|
||||
|
||||
case $template_ in
|
||||
*XXXX) ;;
|
||||
*) fail_ \
|
||||
"invalid template: $template_ (must have a suffix of at least 4 X's)";;
|
||||
esac
|
||||
|
||||
# First, try to use mktemp.
|
||||
d=`unset TMPDIR; { mktemp -d -t -p "$destdir_" "$template_"; } 2>/dev/null` &&
|
||||
|
||||
# The resulting name must be in the specified directory.
|
||||
case $d in "$destdir_slash_"*) :;; *) false;; esac &&
|
||||
|
||||
# It must have created the directory.
|
||||
test -d "$d" &&
|
||||
|
||||
# It must have 0700 permissions. Handle sticky "S" bits.
|
||||
perms=`ls -dgo "$d" 2>/dev/null` &&
|
||||
case $perms in drwx--[-S]---*) :;; *) false;; esac && {
|
||||
echo "$d"
|
||||
return
|
||||
}
|
||||
|
||||
# If we reach this point, we'll have to create a directory manually.
|
||||
|
||||
# Get a copy of the template without its suffix of X's.
|
||||
base_template_=`echo "$template_"|sed 's/XX*$//'`
|
||||
|
||||
# Calculate how many X's we've just removed.
|
||||
template_length_=`echo "$template_" | wc -c`
|
||||
nx_=`echo "$base_template_" | wc -c`
|
||||
nx_=`expr $template_length_ - $nx_`
|
||||
|
||||
err_=
|
||||
i_=1
|
||||
while :; do
|
||||
X_=`rand_bytes_ $nx_`
|
||||
candidate_dir_="$destdir_slash_$base_template_$X_"
|
||||
err_=`mkdir -m 0700 "$candidate_dir_" 2>&1` \
|
||||
&& { echo "$candidate_dir_"; return; }
|
||||
test $MAX_TRIES_ -le $i_ && break;
|
||||
i_=`expr $i_ + 1`
|
||||
done
|
||||
fail_ "$err_"
|
||||
}
|
||||
|
||||
# If you want to override the testdir_prefix_ function,
|
||||
# or to add more utility functions, use this file.
|
||||
test -f "$srcdir/init.cfg" \
|
||||
&& . "$srcdir/init.cfg"
|
||||
|
||||
setup_ "$@"
|
||||
# This trap is here, rather than in the setup_ function, because some
|
||||
# shells run the exit trap at shell function exit, rather than script exit.
|
||||
trap remove_tmp_ 0
|
||||
|
|
@ -1,168 +0,0 @@
|
|||
;;;; job-specifier.scm -- tests for (mcron job-specifier) module
|
||||
;;; Copyright © 2016 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(use-modules (ice-9 match)
|
||||
(srfi srfi-64)
|
||||
(srfi srfi-111)
|
||||
(mcron job-specifier))
|
||||
|
||||
(test-begin "job-specifier")
|
||||
|
||||
(test-equal "range: basic"
|
||||
'(0 1 2 3 4 5 6 7 8 9)
|
||||
(range 0 10))
|
||||
|
||||
(test-equal "range: positive step"
|
||||
'(0 2 4 6 8)
|
||||
(range 0 10 2))
|
||||
|
||||
(test-assert "range: zero step"
|
||||
;; Since this behavior is undefined, only check if range doesn't crash.
|
||||
(range 0 5 0))
|
||||
|
||||
(test-assert "range: negative step"
|
||||
;; Since this behavior is undefined, only check if range doesn't crash.
|
||||
(range 0 5 -2))
|
||||
|
||||
(test-assert "range: reverse boundaries"
|
||||
(range 10 3))
|
||||
|
||||
(define %find-best-next (@@ (mcron job-specifier) %find-best-next))
|
||||
|
||||
(test-assert "%find-best-next: exact"
|
||||
;; Ensure that '%find-best-next' preserves the exactness of the numbers
|
||||
;; inside the NEXT-LIST argument.
|
||||
(match (pk 'match (%find-best-next 1 '(0 2)))
|
||||
((a . b) (and (exact? a) (exact? b)))))
|
||||
|
||||
;;;
|
||||
;;; Check 'next-...' procedures.
|
||||
;;;
|
||||
|
||||
;;; TODO: Find more meaningful date examples.
|
||||
|
||||
(setenv "TZ" ":UTC")
|
||||
|
||||
(test-equal "next-year"
|
||||
(list 1893456000 1546300800)
|
||||
(list (next-year '(130)) ;; This is the year 2030.
|
||||
(next-year-from 1522095469)))
|
||||
|
||||
(test-equal "next-month"
|
||||
5097600
|
||||
(next-month-from 101 '(0 2 4)))
|
||||
|
||||
(test-equal "next-day"
|
||||
345600
|
||||
(next-day-from 4337 '(0 5 10)))
|
||||
|
||||
(test-equal "next-hour"
|
||||
3600
|
||||
(next-hour-from 3 '(0 1 2 3 4)))
|
||||
|
||||
(test-equal "next-minute"
|
||||
60
|
||||
(next-minute-from 8))
|
||||
|
||||
(test-equal "next-second"
|
||||
15
|
||||
(next-second-from 14))
|
||||
|
||||
;;;
|
||||
;;; Check 'configuration-user' manipulation
|
||||
;;;
|
||||
|
||||
(define configuration-user (@@ (mcron job-specifier) configuration-user))
|
||||
|
||||
;;; Call 'set-configuration-user' with a valid uid.
|
||||
(let ((uid (getuid)))
|
||||
(test-equal "set-configuration-user: uid"
|
||||
uid
|
||||
(begin
|
||||
(set-configuration-user uid)
|
||||
(passwd:uid (unbox configuration-user)))))
|
||||
|
||||
(define entry
|
||||
;; Random user entry.
|
||||
(getpw))
|
||||
|
||||
;;; Call 'set-configuration-user' with a valid user name.
|
||||
(let ((name (passwd:name entry)))
|
||||
(test-equal "set-configuration-user: name"
|
||||
name
|
||||
(begin
|
||||
(set-configuration-user name)
|
||||
(passwd:name (unbox configuration-user)))))
|
||||
|
||||
(define root-entry (getpw 0))
|
||||
|
||||
;;; Call 'set-configuration-user' with a passwd entry.
|
||||
(test-equal "set-configuration-user: passwd entry"
|
||||
root-entry
|
||||
(begin
|
||||
(set-configuration-user root-entry)
|
||||
(unbox configuration-user)))
|
||||
|
||||
;;; Call 'set-configuration-user' with an invalid uid.
|
||||
(test-error "set-configuration-user: invalid uid"
|
||||
#t
|
||||
(set-configuration-user -20000))
|
||||
|
||||
;;; Call 'set-configuration-user' with an invalid spec.
|
||||
(test-error "set-configuration-user: invalid spec"
|
||||
#t
|
||||
(set-configuration-user 'wrong))
|
||||
|
||||
;;;
|
||||
;;; Check the 'job' procedure
|
||||
;;;
|
||||
|
||||
(test-assert "job: procedure timeproc"
|
||||
(job 1+ "dummy action"))
|
||||
|
||||
(test-assert "job: list timeproc"
|
||||
(job '(next-hour '(0)) "dummy action"))
|
||||
|
||||
(test-assert "job: string timeproc"
|
||||
(job "30 4 1,15 * 5" "dummy action"))
|
||||
|
||||
(test-error "job: invalid string timeproc"
|
||||
'mcron-error
|
||||
(job "30 4 1,15 * WRONG" "dummy action"))
|
||||
|
||||
(test-error "job: invalid timeproc"
|
||||
'mcron-error
|
||||
(job 42 "dummy action"))
|
||||
|
||||
(test-assert "job: procedure action"
|
||||
(job 1+ (λ () (display "hello\n"))))
|
||||
|
||||
(test-assert "job: list action"
|
||||
(job 1+ '(display "hello\n")))
|
||||
|
||||
(test-assert "job: string action"
|
||||
(job 1+ "echo hello"))
|
||||
|
||||
(test-error "job: string action"
|
||||
'mcron-error
|
||||
(job 1+ 42))
|
||||
|
||||
(test-assert "job: user name"
|
||||
(job 1+ "dummy action" #:user (getuid)))
|
||||
|
||||
(test-end)
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
;;;; redirect.scm -- tests for (mcron redirect) module
|
||||
;;; Copyright © 2020 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(use-modules (ice-9 textual-ports)
|
||||
(srfi srfi-1)
|
||||
(srfi srfi-64)
|
||||
(mcron redirect))
|
||||
|
||||
(setenv "TZ" "UTC0")
|
||||
|
||||
(test-begin "redirect")
|
||||
|
||||
(define out (mkstemp! (string-copy "foo-XXXXXX")))
|
||||
|
||||
(dynamic-wind
|
||||
(const #t)
|
||||
(lambda ()
|
||||
(with-mail-out "echo 'foo'" "user0"
|
||||
#:out (lambda () out)
|
||||
#:hostname "localhost")
|
||||
|
||||
(flush-all-ports)
|
||||
|
||||
(test-equal "mail output"
|
||||
"To: user0
|
||||
From: mcron
|
||||
Subject: user0@localhost
|
||||
|
||||
foo
|
||||
"
|
||||
(call-with-input-file (port-filename out) get-string-all)))
|
||||
|
||||
(lambda ()
|
||||
(let ((fname (port-filename out)))
|
||||
(close out)
|
||||
(delete-file fname))))
|
||||
|
||||
(test-end)
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
# schedule-2.sh -- Check mcron schedule output
|
||||
# Copyright © 2020 Dale Mellor <mcron-lsfnyl@rdmp.org>
|
||||
#
|
||||
# This file is part of GNU Mcron.
|
||||
#
|
||||
# GNU Mcron is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# GNU Mcron is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
source "${srcdir}/tests/init.sh"
|
||||
|
||||
# Use UTC and SOURCE_DATE_EPOCH to get reproducible result.
|
||||
|
||||
SOURCE_DATE_EPOCH=1
|
||||
export SOURCE_DATE_EPOCH
|
||||
|
||||
TZ=UTC0
|
||||
export TZ
|
||||
|
||||
# Use current working directory to store mcron files
|
||||
XDG_CONFIG_HOME=`pwd`
|
||||
export XDG_CONFIG_HOME
|
||||
|
||||
LC_ALL=C
|
||||
export LC_ALL
|
||||
|
||||
mkdir cron
|
||||
cat > cron/foo.guile <<EOF
|
||||
(job '(next-second) '(display "foo\n"))
|
||||
EOF
|
||||
|
||||
cat > expected <<EOF
|
||||
Thu Jan 1 00:00:01 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:02 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:03 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:04 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:05 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:06 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:07 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:08 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
EOF
|
||||
|
||||
mcron -s cron/foo.guile > output
|
||||
diff expected output \
|
||||
|| skip_ 'The -s option is not fully functional;
|
||||
this will be fixed with a future version of GNU Guile.'
|
||||
|
||||
Exit 0
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
# schedule.sh -- Check mcron schedule output
|
||||
# Copyright © 2017, 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
#
|
||||
# This file is part of GNU Mcron.
|
||||
#
|
||||
# GNU Mcron is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# GNU Mcron is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
source "${srcdir}/tests/init.sh"
|
||||
|
||||
# Use UTC and SOURCE_DATE_EPOCH to get reproducible result.
|
||||
|
||||
SOURCE_DATE_EPOCH=1
|
||||
export SOURCE_DATE_EPOCH
|
||||
|
||||
TZ=UTC0
|
||||
export TZ
|
||||
|
||||
LC_ALL=C
|
||||
export LC_ALL
|
||||
|
||||
# Use current working directory to store mcron files
|
||||
XDG_CONFIG_HOME=`pwd`
|
||||
export XDG_CONFIG_HOME
|
||||
|
||||
mkdir cron
|
||||
cat > cron/foo.guile <<EOF
|
||||
(job '(next-second) '(display "foo\n"))
|
||||
EOF
|
||||
|
||||
cat > cron/bar.guile <<EOF
|
||||
(job '(next-second) '(display "bar\n"))
|
||||
EOF
|
||||
|
||||
cat > expected <<EOF
|
||||
Thu Jan 1 00:00:01 1970 +0000
|
||||
(display bar
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:01 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:02 1970 +0000
|
||||
(display bar
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:02 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:03 1970 +0000
|
||||
(display bar
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:03 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:04 1970 +0000
|
||||
(display bar
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:04 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:05 1970 +0000
|
||||
(display bar
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:05 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:06 1970 +0000
|
||||
(display bar
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:06 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:07 1970 +0000
|
||||
(display bar
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:07 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:08 1970 +0000
|
||||
(display bar
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:08 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:09 1970 +0000
|
||||
(display bar
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:09 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:10 1970 +0000
|
||||
(display bar
|
||||
)
|
||||
|
||||
Thu Jan 1 00:00:10 1970 +0000
|
||||
(display foo
|
||||
)
|
||||
|
||||
EOF
|
||||
|
||||
mcron --schedule=10 > output
|
||||
diff expected output || fail_ "schedule output is not correct"
|
||||
|
||||
Exit 0
|
||||
111
tests/utils.scm
111
tests/utils.scm
|
|
@ -1,111 +0,0 @@
|
|||
;;;; utils.scm -- tests for (mcron utils) module
|
||||
;;; Copyright © 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(use-modules (ice-9 match)
|
||||
(ice-9 rdelim)
|
||||
(srfi srfi-64)
|
||||
(mcron config)
|
||||
(mcron utils))
|
||||
|
||||
(test-begin "utils")
|
||||
|
||||
;;; Check 'mcron-error' error code return value.
|
||||
(test-equal "mcron-error: exit code"
|
||||
42
|
||||
(match (primitive-fork)
|
||||
(0 ;child
|
||||
(mcron-error 42 "exit with 42"))
|
||||
((= waitpid (pid . exit-code)) ;parent
|
||||
(status:exit-val exit-code))))
|
||||
|
||||
;;; Check 'mcron-error' output with basic error code.
|
||||
(test-equal "mcron-error: output"
|
||||
"mcron: token"
|
||||
(call-with-output-string
|
||||
(λ (port)
|
||||
(match (pipe)
|
||||
((in . out)
|
||||
(match (primitive-fork)
|
||||
(0 ;child
|
||||
(close in)
|
||||
(with-error-to-port out
|
||||
(λ () (mcron-error 37 "token"))))
|
||||
((= waitpid (pid . exit-code)) ;parent
|
||||
(close out)
|
||||
(display (read-line in) port))))))))
|
||||
|
||||
;;; Check mcron-error output when error code is 0.
|
||||
(test-equal "mcron-error: output no-exit"
|
||||
"mcron: foobar\n"
|
||||
(call-with-output-string
|
||||
(λ (port)
|
||||
(with-error-to-port port
|
||||
(λ ()
|
||||
(mcron-error 0 "foo" "bar"))))))
|
||||
|
||||
;;; Check that mcron-error doesn't print anything on the standard output.
|
||||
(test-equal "mcron-error: only stderr"
|
||||
""
|
||||
(with-output-to-string
|
||||
(λ () (mcron-error 0 "foo" "bar"))))
|
||||
|
||||
;;;
|
||||
;;; Check user interface conformance to GNU Coding Standards
|
||||
;;;
|
||||
|
||||
(test-assert "show-version"
|
||||
(let ((out (with-output-to-string (λ () (show-version "dummy")))))
|
||||
(and (string-contains out config-package-version)
|
||||
(string-contains out config-package-name))))
|
||||
|
||||
(test-assert "show-package-information"
|
||||
(let ((out (with-output-to-string (λ () (show-package-information)))))
|
||||
(string-contains out config-package-bugreport)))
|
||||
|
||||
;;;
|
||||
;;; Check 'get-user'
|
||||
;;;
|
||||
|
||||
(define entry
|
||||
;; Random user entry.
|
||||
(getpw))
|
||||
|
||||
;;; Call 'get-user' with a valid uid.
|
||||
(let ((uid (getuid)))
|
||||
(test-equal "get-user: uid"
|
||||
uid
|
||||
(passwd:uid (get-user uid))))
|
||||
|
||||
;;; Call 'get-user' with a valid user name.
|
||||
(let ((name (passwd:name entry)))
|
||||
(test-equal "get-user: name"
|
||||
name
|
||||
(passwd:name (get-user name))))
|
||||
|
||||
;;; Call 'get-user' with a passwd entry.
|
||||
(test-equal "get-user: passwd entry"
|
||||
entry
|
||||
(get-user entry))
|
||||
|
||||
;;; Call 'get-user' with an invalid uid.
|
||||
(test-error "get-user: invalid uid" #t (get-user -20000))
|
||||
|
||||
;;; Call 'get-user' with an invalid spec.
|
||||
(test-error "get-user: invalid spec" #t (get-user 'wrong))
|
||||
|
||||
(test-end)
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
;;;; vixie-specification.scm -- tests for (mcron vixie-specificaion) module
|
||||
;;; Copyright © 2020 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(use-modules (srfi srfi-1)
|
||||
(srfi srfi-64)
|
||||
(mcron vixie-specification))
|
||||
|
||||
(setenv "TZ" "UTC0")
|
||||
|
||||
;;; Do not send mail
|
||||
(setenv "MAILTO" "")
|
||||
|
||||
(define (create-file! content)
|
||||
"Construct a temporary file port containing CONTENT which must be a string."
|
||||
(let ((port (mkstemp! (string-copy "file-XXXXXX"))))
|
||||
(display content port)
|
||||
(force-output port)
|
||||
port))
|
||||
|
||||
(define (clean-temp port)
|
||||
"Close and Delete a temporary file port"
|
||||
(let ((fname (port-filename port)))
|
||||
(close port)
|
||||
(delete-file fname)))
|
||||
|
||||
(define schedule (@@ (mcron base) %global-schedule))
|
||||
(define schedule-user (@@ (mcron base) schedule-user))
|
||||
(define set-schedule-user! (@@ (mcron base) set-schedule-user!))
|
||||
(define job:environment (@@ (mcron base) job:environment))
|
||||
(define job:displayable (@@ (mcron base) job:displayable))
|
||||
(define job:user (@@ (mcron base) job:user))
|
||||
|
||||
(test-begin "vixie-specification")
|
||||
|
||||
;;; Parse user crontab file
|
||||
|
||||
(define user-crontab-example
|
||||
"# Example crontab
|
||||
FOO=x
|
||||
BAR=y
|
||||
|
||||
# Example of job definitions:
|
||||
17 * * * * cd / && run baz
|
||||
47 6 * * 7 foo -x /tmp/example || bar
|
||||
")
|
||||
|
||||
(define user-crontab (create-file! user-crontab-example))
|
||||
|
||||
(dynamic-wind
|
||||
(const #t)
|
||||
(lambda ()
|
||||
(set-schedule-user! schedule '())
|
||||
(read-vixie-file (port-filename user-crontab))
|
||||
|
||||
(test-assert "User schedule has exactly 2 matching jobs"
|
||||
(lset= string=?
|
||||
'("cd / && run baz"
|
||||
"foo -x /tmp/example || bar")
|
||||
(map job:displayable (schedule-user schedule))))
|
||||
|
||||
(test-assert "Job environment matches configuration"
|
||||
(every (lambda (j)
|
||||
(lset= equal?
|
||||
'(("FOO" . "x") ("BAR" . "y"))
|
||||
(job:environment j)))
|
||||
(schedule-user schedule))))
|
||||
|
||||
(lambda ()
|
||||
(clean-temp user-crontab)))
|
||||
|
||||
;;; Parse system crontab file
|
||||
|
||||
;;; Get two existing users from the test environment.
|
||||
(setpwent)
|
||||
(define user0 (getpwent))
|
||||
(define user1 (or (getpwent) user0))
|
||||
(define system-crontab-example
|
||||
(string-append
|
||||
"# Example crontab
|
||||
BAZ=z
|
||||
|
||||
17 * * * * " (passwd:name user0) " cd / && run baz
|
||||
47 6 * * 7 " (passwd:name user1) " foo -x /tmp/example || bar"))
|
||||
|
||||
(define sys-crontab (create-file! system-crontab-example))
|
||||
|
||||
(dynamic-wind
|
||||
(const #t)
|
||||
(lambda ()
|
||||
(set-schedule-user! schedule '())
|
||||
(read-vixie-file (port-filename sys-crontab) parse-system-vixie-line)
|
||||
|
||||
(test-assert "System schedule has exactly 2 matching jobs"
|
||||
(lset= equal?
|
||||
`((,user0 . "cd / && run baz")
|
||||
(,user1 . "foo -x /tmp/example || bar"))
|
||||
(map (lambda (j)
|
||||
(cons (job:user j) (job:displayable j)))
|
||||
(schedule-user schedule))))
|
||||
|
||||
(test-assert "Job environment matches configuration"
|
||||
(every (lambda (j)
|
||||
(lset= equal? '(("BAZ" . "z")) (job:environment j)))
|
||||
(schedule-user schedule))))
|
||||
|
||||
(lambda ()
|
||||
(clean-temp sys-crontab)))
|
||||
|
||||
;;; Try to parse a user crontab in a system context
|
||||
|
||||
(define wrong-system-crontab-example
|
||||
"
|
||||
# Example of job definitions:
|
||||
17 * * * * ls")
|
||||
|
||||
(define wrong-sys-crontab (create-file! wrong-system-crontab-example))
|
||||
|
||||
(dynamic-wind
|
||||
(const #t)
|
||||
(lambda ()
|
||||
(test-error "missing user"
|
||||
'mcron-error
|
||||
(read-vixie-file (port-filename wrong-sys-crontab)
|
||||
parse-system-vixie-line)))
|
||||
|
||||
(lambda ()
|
||||
(clean-temp wrong-sys-crontab)))
|
||||
|
||||
(test-end)
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
;;;; vixie-time.scm -- tests for (mcron vixie-time) module
|
||||
;;; Copyright © 2018 Mathieu Lirzin <mthl@gnu.org>
|
||||
;;;
|
||||
;;; This file is part of GNU Mcron.
|
||||
;;;
|
||||
;;; GNU Mcron is free software: you can redistribute it and/or modify
|
||||
;;; it under the terms of the GNU General Public License as published by
|
||||
;;; the Free Software Foundation, either version 3 of the License, or
|
||||
;;; (at your option) any later version.
|
||||
;;;
|
||||
;;; GNU Mcron is distributed in the hope that it will be useful,
|
||||
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;; GNU General Public License for more details.
|
||||
;;;
|
||||
;;; You should have received a copy of the GNU General Public License
|
||||
;;; along with GNU Mcron. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
(use-modules (srfi srfi-1)
|
||||
(srfi srfi-64)
|
||||
(mcron vixie-time))
|
||||
|
||||
(setenv "TZ" "UTC0")
|
||||
|
||||
(test-begin "vixie-time")
|
||||
|
||||
(define (times-equal spec times proc)
|
||||
(test-equal spec
|
||||
(cdr times)
|
||||
(fold-right (λ (val acc)
|
||||
(cons (proc val) acc))
|
||||
'()
|
||||
(drop-right times 1))))
|
||||
|
||||
(times-equal
|
||||
"every minute"
|
||||
'(0 60 120 180 240 300 360 420)
|
||||
(parse-vixie-time "* * * * *"))
|
||||
|
||||
(times-equal
|
||||
"every hour"
|
||||
(list 0
|
||||
3600
|
||||
(* 2 3600)
|
||||
(* 3 3600)
|
||||
(* 4 3600)
|
||||
(* 5 3600)
|
||||
(* 6 3600)
|
||||
(* 7 3600))
|
||||
(parse-vixie-time "0 * * * *"))
|
||||
|
||||
(times-equal
|
||||
"every day"
|
||||
(list 0
|
||||
(* 24 3600)
|
||||
(* 2 24 3600)
|
||||
(* 3 24 3600)
|
||||
(* 4 24 3600)
|
||||
(* 5 24 3600)
|
||||
(* 6 24 3600)
|
||||
(* 7 24 3600))
|
||||
(parse-vixie-time "0 0 * * *"))
|
||||
|
||||
(times-equal
|
||||
"every month"
|
||||
(list 0
|
||||
(* 31 86400) ;jan
|
||||
(* (+ 31 28) 86400) ;fev
|
||||
(* (+ 31 28 31) 86400) ;mar
|
||||
(* (+ 31 28 31 30) 86400) ;avr
|
||||
(* (+ 31 28 31 30 31) 86400) ;may
|
||||
(* (+ 31 28 31 30 31 30) 86400) ;jun
|
||||
(* (+ 31 28 31 30 31 30 31) 86400)) ;july
|
||||
(parse-vixie-time "0 0 1 * *"))
|
||||
|
||||
(times-equal
|
||||
"every year"
|
||||
(list 0
|
||||
(* 365 86400) ;1971
|
||||
(* 2 365 86400) ;1972 (leap)
|
||||
(* (+ (* 2 365) 366) 86400) ;1973
|
||||
(* (+ (* 3 365) 366) 86400) ;1974
|
||||
(* (+ (* 4 365) 366) 86400) ;1975
|
||||
(* (+ (* 5 365) 366) 86400) ;1976 (leap)
|
||||
(* (+ (* 5 365) (* 2 366)) 86400)) ;1977
|
||||
(parse-vixie-time "0 0 1 0 *"))
|
||||
|
||||
(times-equal
|
||||
"30 4 1,15 * 5"
|
||||
(list 0
|
||||
(+ (* 4 3600) 1800)
|
||||
(+ (* 28 3600) 1800)
|
||||
(+ (* 8 86400) (* 4 3600) 1800)
|
||||
(+ (* 13 86400) (* 28 3600) 1800)
|
||||
(+ (* 15 86400) (* 4 3600) 1800)
|
||||
(+ (* 532 3600) 1800))
|
||||
(parse-vixie-time "30 4 1,15 * 5"))
|
||||
|
||||
;;;
|
||||
;;; Errors
|
||||
;;;
|
||||
|
||||
;; FIXME: infinite loop
|
||||
;; (test-error "month 0" #t
|
||||
;; (let ((p (parse-vixie-time "0 0 0 * *")))
|
||||
;; (p 1234)))
|
||||
|
||||
(test-error
|
||||
"not enough fields"
|
||||
'mcron-error
|
||||
(parse-vixie-time "1 2 3 4"))
|
||||
|
||||
(test-error
|
||||
"too many fields"
|
||||
'mcron-error
|
||||
(parse-vixie-time "1 2 3 4 5 6"))
|
||||
|
||||
(test-end)
|
||||
Loading…
Add table
Add a link
Reference in a new issue