├── .github ├── dependabot.yml └── workflows │ ├── codespell.yml │ └── linux.yml ├── .gitignore ├── .packit.yaml ├── AUTHORS ├── COPYING ├── COPYING.anacron ├── COPYING.obstack ├── ChangeLog ├── INSTALL ├── Makefile.am ├── NEWS ├── README ├── README.anacron ├── anacron ├── ChangeLog.anacron ├── Makemodule.am ├── global.h ├── gregor.c ├── gregor.h ├── lock.c ├── log.c ├── main.c ├── matchrx.c ├── matchrx.h ├── readtab.c └── runjob.c ├── autogen.sh ├── configure.ac ├── contrib ├── 0anacron ├── 0hourly ├── anacrontab ├── cronie.systemd └── dailyjobs ├── crond.sysconfig ├── cronie.init ├── cronie.spec ├── cronie_common.c ├── cronie_common.h ├── man ├── Makemodule.am ├── anacron.8 ├── anacrontab.5 ├── cron.8 ├── crond.8 ├── cronnext.1 ├── crontab.1 └── crontab.5 ├── obstack ├── obstack.c └── obstack.h ├── pam └── crond ├── readme.md └── src ├── .dirstamp ├── .indent.pro ├── Makemodule.am ├── bitstring.h ├── cron.c ├── cronnext.c ├── crontab.c ├── database.c ├── do_command.c ├── entry.c ├── env.c ├── externs.h ├── funcs.h ├── globals.h ├── job.c ├── macros.h ├── misc.c ├── pathnames.h ├── popen.c ├── pw_dup.c ├── security.c ├── structs.h └── user.c /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Sebastian Pipping 2 | # Licensed under GPL v2 or later 3 | 4 | version: 2 5 | updates: 6 | 7 | - package-ecosystem: "github-actions" 8 | commit-message: 9 | include: "scope" 10 | prefix: "Actions" 11 | directory: "/" 12 | labels: 13 | - "enhancement" 14 | schedule: 15 | interval: "weekly" 16 | -------------------------------------------------------------------------------- /.github/workflows/codespell.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Sebastian Pipping 2 | # Licensed under GPL v2 or later 3 | 4 | name: Enforce codespell-clean spelling 5 | 6 | on: 7 | pull_request: 8 | push: 9 | schedule: 10 | - cron: '0 3 * * 5' # Every Friday at 3am 11 | workflow_dispatch: 12 | 13 | # Minimum permissions for security 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | codespell: 19 | name: Enforce codespell-clean spelling 20 | runs-on: ubuntu-22.04 21 | steps: 22 | - uses: actions/checkout@v4 23 | 24 | - uses: codespell-project/actions-codespell@v2 25 | with: 26 | # "accreting" is the gerund of verb "to accrete" (file obstack/obstack.h) 27 | # "annualy" is a known backwards compat misspelling (file anacron/readtab.c) 28 | # "dows" is plural of "day of the week" in crontab syntax (file src/entry.c) 29 | # "vas" is from "Jason Vas Dias" of Red Hat, Inc. (file src/security.c) 30 | # Words need to be (1) separated by a comma and (2) all lowercase! 31 | ignore_words_list: accreting,annualy,dows,vas,triggerin 32 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 Sebastian Pipping 2 | # Licensed under GPL v2 or later 3 | 4 | name: Build on Linux 5 | 6 | on: 7 | pull_request: 8 | push: 9 | schedule: 10 | - cron: '0 3 * * 5' # Every Friday at 3am 11 | workflow_dispatch: 12 | 13 | # Minimum permissions for security 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | linux: 19 | name: Build (${{ matrix.cc }}) 20 | runs-on: ${{ matrix.runs-on }} 21 | strategy: 22 | fail-fast: false 23 | matrix: 24 | include: 25 | - cc: gcc-13 26 | cxx: g++-13 27 | clang_major_version: null 28 | clang_repo_suffix: null 29 | runs-on: ubuntu-22.04 30 | - cc: clang-17 31 | cxx: clang++-17 32 | clang_major_version: 17 33 | clang_repo_suffix: -17 34 | runs-on: ubuntu-22.04 35 | steps: 36 | - name: Checkout Git branch 37 | uses: actions/checkout@v4 38 | 39 | - name: Add Clang/LLVM repositories 40 | if: "${{ contains(matrix.cc, 'clang') }}" 41 | run: |- 42 | set -x 43 | source /etc/os-release 44 | wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - 45 | sudo add-apt-repository "deb http://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}${{ matrix.clang_repo_suffix }} main" 46 | 47 | - name: Install build dependencies 48 | run: |- 49 | sudo apt-get update 50 | sudo apt-get install --yes --no-install-recommends \ 51 | libaudit-dev \ 52 | libpam0g-dev \ 53 | libselinux1-dev 54 | 55 | - name: Install build dependency Clang ${{ matrix.clang_major_version }} 56 | if: "${{ contains(matrix.cc, 'clang') }}" 57 | run: |- 58 | sudo apt-get install --yes --no-install-recommends -V \ 59 | clang-${{ matrix.clang_major_version }} 60 | 61 | - name: 'Bootstrap' 62 | run: |- 63 | ./autogen.sh 64 | 65 | - name: 'Configure' 66 | env: 67 | CFLAGS: '-std=gnu99 -Wall -Wextra -pedantic -O1 -pipe' 68 | LDFLAGS: '-Wl,--as-needed' 69 | run: |- 70 | set -x 71 | mkdir build 72 | cd build 73 | configure_args=( 74 | # Make build logs better explain themselves 75 | --disable-silent-rules 76 | 77 | # Enable more optional features to increase coverage 78 | --enable-syscrontab 79 | --with-audit 80 | --with-inotify 81 | --with-pam 82 | --with-selinux 83 | ) 84 | ../configure "${configure_args[@]}" 85 | 86 | - name: 'Make' 87 | run: |- 88 | make -C build -j$(nproc) 89 | 90 | - name: 'Install' 91 | run: |- 92 | set -x -o pipefail 93 | make -C build install DESTDIR="${PWD}"/ROOT/ 94 | find ROOT/ | sort | xargs ls -ld 95 | 96 | - name: 'Distcheck' 97 | run: |- 98 | set -x 99 | make -C build distcheck 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile.in 2 | Makefile 3 | anacron/anacron 4 | anacron-paths.h 5 | anacron/Makefile 6 | anacron/Makefile.in 7 | aclocal.m4 8 | autom4te.cache 9 | compile 10 | config.guess 11 | config.h 12 | config.h.in 13 | config.log 14 | config.status 15 | config.sub 16 | configure 17 | depcomp 18 | install-sh 19 | man/Makefile 20 | man/Makefile.in 21 | missing 22 | stamp-h1 23 | tags 24 | .deps 25 | *.o 26 | src/crond 27 | src/crontab 28 | src/cronnext 29 | cron-paths.h 30 | *~ 31 | *.tar.* 32 | *.orig 33 | *.patch 34 | .dirstamp 35 | -------------------------------------------------------------------------------- /.packit.yaml: -------------------------------------------------------------------------------- 1 | downstream_package_name: cronie 2 | jobs: 3 | - job: copr_build 4 | metadata: 5 | targets: 6 | - fedora-32-x86_64 7 | - fedora-33-x86_64 8 | - fedora-rawhide-x86_64 9 | trigger: pull_request 10 | specfile_path: cronie.spec 11 | files_to_sync: 12 | - cronie.spec 13 | - .packit.yaml 14 | upstream_package_name: cronie 15 | srpm_build_deps: 16 | - gcc 17 | - systemd 18 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Original vixie-cron was written by Paul Vixie. 2 | 3 | Significant contributors: 4 | Marcela Mašláňová 5 | Colin Dean 6 | Tomáš Mráz 7 | Marco Migliori 8 | Sami Kerola 9 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Basic Installation 2 | ================== 3 | 4 | Run the usual autotools combination of: 5 | ./configure 6 | make 7 | make install 8 | 9 | If no "configure" script is present, generate it by: 10 | ./autogen.sh 11 | 12 | The executable files will be installed in /usr/local/* by default. 13 | 14 | Configure Options 15 | ================= 16 | Please see the ./configure --help output for available build-time 17 | options. 18 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CFLAGS = -I$(top_srcdir) 2 | 3 | BUILT_SOURCES = 4 | CLEANFILES = 5 | EXTRA_DIST = 6 | bin_PROGRAMS = 7 | common_nodist = 8 | sbin_PROGRAMS = 9 | 10 | dist_noinst_HEADERS = \ 11 | cronie_common.h 12 | 13 | EXTRA_DIST += \ 14 | README.anacron \ 15 | COPYING.anacron \ 16 | COPYING.obstack \ 17 | obstack/obstack.h \ 18 | cronie.init \ 19 | crond.sysconfig \ 20 | contrib/anacrontab \ 21 | contrib/0anacron \ 22 | contrib/0hourly \ 23 | contrib/dailyjobs \ 24 | contrib/cronie.systemd \ 25 | anacron/ChangeLog.anacron 26 | 27 | if PAM 28 | pamdir = $(sysconfdir)/pam.d 29 | dist_pam_DATA = pam/crond 30 | else 31 | EXTRA_DIST += pam/crond 32 | endif 33 | 34 | include anacron/Makemodule.am 35 | include man/Makemodule.am 36 | include src/Makemodule.am 37 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | cronie NEWS -- history of user-visible changes. 2 | 3 | Release 1.7.2 4 | 5 | * crond: Revert setting the return path to <>. It is not RFC compliant. 6 | * crond: Inherit MAILFROM from the crond process environment. 7 | 8 | Release 1.7.1 9 | 10 | * crond: Wait on finishing the job with -n option to check 11 | the exit status 12 | * crond: Do not set the return path to <> if non-default MAILFROM is set 13 | * /etc/sysconfig/crond and /etc/default/anacron files are optional 14 | 15 | Release 1.7.0 16 | 17 | * anacron: Add support for NO_MAIL_OUTPUT environment variable 18 | * anacron: Support enabling anacron jobs on battery power 19 | * crond: Support -n crontab entry option to disable mailing the output 20 | * crontab: Make a backup of the crontab file on edition and deletion 21 | 22 | Release 1.6.1 23 | 24 | * crond: Fix regression of handling ranges (x-y) in crontab 25 | 26 | Release 1.6.0 27 | 28 | * crond: Add switch -f as an alias for -n 29 | * crond: Add random within range '~' operator 30 | * crond: Use the configure runstatedir directory for pid file 31 | * crond: Increase the maximum number of crontab entries to 10000 32 | 33 | Release 1.5.7 34 | 35 | * anacron: Fix problem of anacron not being started on some desktops 36 | * crontab: switch off colors if NO_COLOR is set 37 | 38 | Release 1.5.6 39 | 40 | * crontab: crontab without arguments now works if stdin is not a TTY 41 | * crond: Fix various issues on loading the crontab databases on startup 42 | * anacron: Expand MAILTO and MAILFROM environment variables 43 | * crontab: New option to test crontab file syntax without installing it 44 | 45 | Release 1.5.5 46 | 47 | * Explicitly validate upper end of range and step to disallow entries 48 | such as: 1-234/5678 * * * * .... 49 | * crond: Report missing newline before EOF in syslog so the line is not 50 | completely silently ignored. 51 | * crontab -l colors comment lines in a different color. 52 | * crond: Revert "Avoid creating pid files when crond doesn't fork". 53 | * anacron is built by default. 54 | * Use non-recursive build. 55 | * cronnext: Allow to optionally select jobs by substring. 56 | 57 | Release 1.5.4 58 | 59 | * crond: Fix regression from previous release. Only first job from a crontab 60 | was being run. 61 | 62 | Release 1.5.3 63 | 64 | * Fix CVE-2019-9704 and CVE-2019-9705 to avoid local DoS of the crond. 65 | * crontab: Make crontab without arguments fail. 66 | * crond: In PAM configuration include system-auth instead of password-auth. 67 | * crond: In the systemd service file restart crond if it fails. 68 | * crond: Use the role from the crond context for system job contexts. 69 | * Multiple small cleanups and fixes. 70 | 71 | Release 1.5.2 72 | 73 | * cronnext: New useful utility to find out time of the next job run. 74 | * crond: Avoid creating PID files when crond doesn't fork. 75 | * crontab: Do not try to replace the crontab with a directory. 76 | * crond: Log startup even when started in non-forking mode. 77 | * Multiple small cleanups and fixes. 78 | 79 | Release 1.5.1 80 | 81 | * crontab: Use temporary file name that is ignored by crond. 82 | * crond: Inherit PATH from the crond environment if -P option is used. 83 | * crond: Remove hardcoded "system_u" SELinux user, use the SELinux user 84 | of the running crond. 85 | * anacron: Small cleanups and fixes. 86 | * crond: Fix longstanding race condition on repeated crontab modification. 87 | 88 | Release 1.5.0 89 | 90 | * First release with NEWS. :) 91 | * crond: Job environment variables are set also when executing sendmail. 92 | * crond: Adding duplicate orphans on reload is now prevented. 93 | * crond: The regular crond shutdown is now logged. 94 | * crontab: PAM is not called in crontab command if the caller's uid is 0. 95 | * crond: PAM is not called from crond for system cron jobs 96 | (/etc/crontab, /etc/cron.d) which are run for uid 0. 97 | * crond: The existence of an user is checked at time when job is run 98 | and not when the crontab is parsed on database reload. 99 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Cronie contains the standard UNIX daemon crond that runs specified programs at 2 | scheduled times and related tools. The source is based on the original vixie-cron 3 | and has security and configuration enhancements like the ability to use pam and 4 | SELinux. 5 | 6 | And why cronie? See http://www.urbandictionary.com/define.php?term=cronie 7 | 8 | Contact 9 | ------- 10 | 11 | Mailing list: `cronie-devel AT lists.fedorahosted DOT org` 12 | 13 | Bug reports and pull requests can be filled at the Github site. 14 | -------------------------------------------------------------------------------- /README.anacron: -------------------------------------------------------------------------------- 1 | 2 | What is Anacron ? 3 | ----------------- 4 | 5 | Anacron is a periodic command scheduler. It executes commands at 6 | intervals specified in days. Unlike cron, it does not assume that the 7 | system is running continuously. It can therefore be used to control 8 | the execution of daily, weekly and monthly jobs (or anything with a 9 | period of n days), on systems that don't run 24 hours a day. When 10 | installed and configured properly, Anacron will make sure that the 11 | commands are run at the specified intervals as closely as 12 | machine-uptime permits. 13 | 14 | Every time Anacron is run, it reads a configuration file that 15 | specifies the jobs Anacron controls, and their periods in days. If a 16 | job wasn't executed in the last n days, where n is the period of that 17 | job, Anacron executes it. Anacron then records the date in a special 18 | timestamp file that it keeps for each job, so it can know when to run 19 | it again. When all the executed commands terminate, Anacron exits. 20 | 21 | It is recommended to run Anacron from the system boot-scripts. 22 | This way the jobs "whose time has come" will be run shortly after the 23 | machine boots. A delay can be specified for each job so that the 24 | machine isn't overloaded at boot time. 25 | 26 | In addition to running Anacron from the boot-scripts, it is also 27 | recommended to schedule it as a daily cron-job (usually at an early 28 | morning hour), so that if the machine is kept running for a night, 29 | jobs for the next day will still be executed. 30 | 31 | 32 | Why this may be useful ? 33 | ------------------------ 34 | 35 | Most Unix-like systems have daily, weekly and monthly scripts that 36 | take care of various "housekeeping chores" such as log-rotation, 37 | updating the "locate" and "man" databases, etc. Daily scripts are 38 | usually scheduled as cron-jobs to execute around 1-7 AM. Weekly 39 | scripts are scheduled to run on Sundays. On machines that are turned 40 | off for the night or for the weekend, these scripts rarely get run. 41 | 42 | Anacron solves this problem. These jobs can simply be scheduled as 43 | Anacron-jobs with periods of 1, 7 and a special target called @monthly. 44 | 45 | 46 | What Anacron is not ? 47 | --------------------- 48 | 49 | Anacron is not an attempt to make cron redundant. It cannot 50 | currently be used to schedule commands at intervals smaller than days. 51 | It also does not guarantee that the commands will be executed at any 52 | specific day or hour. 53 | 54 | It isn't a full-time daemon. It has to be executed from boot 55 | scripts, from cron-jobs, or explicitly. 56 | 57 | 58 | For more details, see the anacron(8) manpage. 59 | 60 | 61 | Requirements 62 | ------------ 63 | 64 | - A Linux system. (maybe other *NIX systems) 65 | - A functioning syslog daemon. 66 | - A functioning /usr/lib/sendmail command. (all MTAs should have 67 | that). 68 | 69 | 70 | Compilation and Installation 71 | ---------------------------- 72 | 73 | - Untar the source package. 74 | 75 | - Check the Makefile. Edit as required. 76 | 77 | - Check the top of "global.h". You may want to change the syslog 78 | facility and priorities, and the path to your MTA's sendmail 79 | compatible command (/usr/lib/sendmail). 80 | 81 | - cd to the directory. 82 | 83 | - Type "make". 84 | You can safely ignore warnings of the form: "*.d: No such file or 85 | directory" 86 | 87 | - Become root. Type "make install". 88 | 89 | 90 | Setup 91 | ----- 92 | 93 | 1. Locate your system's daily, weekly and monthly cron-jobs. 94 | See your cron documentation for more details. 95 | 96 | 2. Decide which of these jobs should be controlled by Anacron. 97 | Remember that Anacron does not guarantee execution at any specific 98 | day of the month, day of the week, or time of day. Jobs for which 99 | the timing is critical should probably not be controlled by 100 | Anacron. 101 | 102 | 3. Comment these jobs out of their crontab files. (You may have to 103 | use the "crontab" command for this. See the cron documentation.) 104 | 105 | 4. Put them in /etc/anacrontab. Note that the format is not the same 106 | as the crontab entries. See the anacrontab(5) manpage. Here's an 107 | example from a typical Debian system: 108 | 109 | -----Cut 110 | # /etc/anacrontab example 111 | SHELL=/bin/sh 112 | PATH=/sbin:/bin:/usr/sbin:/usr/bin 113 | # format: period delay job-identifier command 114 | 1 5 cron.daily run-parts /etc/cron.daily 115 | 7 10 cron.weekly run-parts /etc/cron.weekly 116 | @monthly 15 cron.monthly run-parts /etc/cron.monthly 117 | -----Cut 118 | 119 | 5. Put the command "anacron -s" somewhere in your boot-scripts. 120 | Make sure that syslogd is started before this command. 121 | 122 | 6. Schedule the command "anacron -s" as a daily cron-job (preferably 123 | at some early morning hour). This will make sure that jobs are run 124 | when the systems is left running for a night. 125 | 126 | That's it. 127 | 128 | It is a good idea to check what your daily, weekly and monthly scripts 129 | actually do, and disable any parts that may be irrelevant for your 130 | system. 131 | 132 | 133 | Credits 134 | ------- 135 | 136 | Anacron was originally conceived and implemented by Christian Schwarz 137 | . 138 | 139 | The current implementation is a complete rewrite by Itai Tzur 140 | . 141 | 142 | Current code base maintained by Sean 'Shaleh' Perry . 143 | -------------------------------------------------------------------------------- /anacron/ChangeLog.anacron: -------------------------------------------------------------------------------- 1 | Changes in Anacron 2.3.1 2 | ------------------------ 3 | * documentation no longer suggests adding local directories to the PATH 4 | 5 | 6 | Changes in Anacron 2.3 7 | ---------------------- 8 | * anacron can now read an arbitrary anacrontab file, use the -t option 9 | 10 | 11 | Changes in Anacron 2.1/2.2 12 | -------------------------- 13 | * Sean 'Shaleh' Perry is now maintainer 14 | * if timestamp is from the future, re-run job 15 | * ansi cleanup / code cleaning 16 | 17 | 18 | Changes in Anacron 2.0.1 19 | ------------------------ 20 | * Minor cosmetic changes to log messages. 21 | * Jobs are now started with "/" as their working directory. This is 22 | more compatible with older Anacron versions, avoids annoying errors on 23 | some systems, and generally seems to make more sense. 24 | 25 | 26 | Summary of major changes in Anacron 2.0 27 | --------------------------------------- 28 | * Complete rewrite in C. Should be backwards compatible with existing 29 | Anacron installations. 30 | * First release as a "generic" Linux package (was a Debian package). 31 | * No longer needs special lock-files. Locking is done on the timestamp 32 | files. 33 | * Sends log messages to syslogd. There's no log file now. 34 | * Output of jobs, if any, is mailed to the user. 35 | * Added command line options: -s -f -n -d -q -u -V -h. See the manpage. 36 | * Specific jobs can now be selected on the command line. 37 | * Added SIGUSR1 handling, to cleanly stop execution. 38 | * Jobs will now be started with their current directory set to the home 39 | of the user running Anacron (usually root). 40 | -------------------------------------------------------------------------------- /anacron/Makemodule.am: -------------------------------------------------------------------------------- 1 | # Makefile.am - two binaries crond and crontab 2 | if ANACRON 3 | sbin_PROGRAMS += anacron/anacron 4 | anacron_anacron_SOURCES = \ 5 | anacron-paths.h \ 6 | anacron/global.h \ 7 | anacron/gregor.c \ 8 | anacron/gregor.h \ 9 | anacron/lock.c \ 10 | anacron/log.c \ 11 | anacron/main.c \ 12 | anacron/matchrx.c \ 13 | anacron/matchrx.h \ 14 | anacron/readtab.c \ 15 | anacron/runjob.c \ 16 | cronie_common.c 17 | common_nodist += anacron-paths.h 18 | nodist_anacron_anacron_SOURCES = $(common_nodist) 19 | BUILT_SOURCES += $(common_nodist) 20 | 21 | if NEED_OBSTACK 22 | anacron_anacron_SOURCES += obstack/obstack.c 23 | endif 24 | 25 | anacron_anacron_LDADD = $(LIBSELINUX) $(LIBPAM) $(LIBAUDIT) 26 | 27 | # This header contains all the paths. 28 | # If they are configurable, they are declared in configure script. 29 | # Depends on this Makefile, because it uses make variables. 30 | CLEANFILES += anacron-paths.h 31 | anacron-paths.h: Makefile 32 | @echo 'creating $@' 33 | @sed >$@ 's/ *\\$$//' <<\END #\ 34 | /* This file has been automatically generated. Do not edit. */ \ 35 | \ 36 | #ifndef _ANACRON_PATHS_H_ \ 37 | #define _ANACRON_PATHS_H_ \ 38 | #define ANACRON_SPOOL_DIR "$(ANACRON_SPOOL_DIR)" \ 39 | #define ANACRONTAB "$(ANACRONTAB)" \ 40 | #endif /* _ANACRON_PATHS_H_ */ \ 41 | END 42 | endif 43 | -------------------------------------------------------------------------------- /anacron/global.h: -------------------------------------------------------------------------------- 1 | /* 2 | Anacron - run commands periodically 3 | Copyright (C) 1998 Itai Tzur 4 | Copyright (C) 1999 Sean 'Shaleh' Perry 5 | Copyright (C) 2004 Pascal Hakim 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | The GNU General Public License can also be found in the file 22 | `COPYING' that comes with the Anacron source distribution. 23 | */ 24 | 25 | #ifndef _ANACRON_GLOBAL_H 26 | #define _ANACRON_GLOBAL_H 27 | 28 | /* Syslog facility and priorities messages will be logged to (see syslog(3)). 29 | * If you change these, please update the man page. */ 30 | #define SYSLOG_FACILITY LOG_CRON 31 | #define EXPLAIN_LEVEL LOG_NOTICE /* informational messages */ 32 | #define COMPLAIN_LEVEL LOG_ERR /* error messages */ 33 | #define DEBUG_LEVEL LOG_DEBUG /* only used when DEBUG is defined */ 34 | 35 | /* Mail interface. (All MTAs should supply this command) */ 36 | #define SENDMAIL "/usr/sbin/sendmail" 37 | 38 | /* End of user-configurable section */ 39 | 40 | 41 | #define FAILURE_EXIT 1 42 | #define MAX_MSG 150 43 | 44 | #include 45 | #include 46 | #include "anacron-paths.h" 47 | #include "cronie_common.h" 48 | 49 | /* Some declarations */ 50 | 51 | struct env_rec1 { 52 | char *assign; 53 | 54 | struct env_rec1 *next; 55 | }; 56 | typedef struct env_rec1 env_rec; 57 | 58 | struct job_rec1 { 59 | int period; 60 | int named_period; 61 | int delay; 62 | char *ident; 63 | char *command; 64 | char *mailto; 65 | int no_mail_output; 66 | 67 | int tab_line; 68 | int arg_num; 69 | int timestamp_fd; 70 | int input_fd; 71 | int output_fd; 72 | off_t mail_header_size; 73 | pid_t job_pid; 74 | pid_t mailer_pid; 75 | int drop_job; 76 | 77 | struct job_rec1 *next; 78 | env_rec *prev_env_rec; 79 | }; 80 | typedef struct job_rec1 job_rec; 81 | 82 | /* Global variables */ 83 | 84 | extern pid_t primary_pid; 85 | extern char *program_name; 86 | extern char *anacrontab; 87 | extern char *spooldir; 88 | extern int old_umask; 89 | extern sigset_t old_sigmask; 90 | extern int serialize,force,update_only,now,no_daemon,quiet,testing_only; 91 | extern int day_now; 92 | extern int year,month,day_of_month; 93 | extern int in_background; 94 | 95 | extern job_rec *first_job_rec; 96 | extern env_rec *first_env_rec; 97 | 98 | extern char **job_args; 99 | extern int job_nargs; 100 | 101 | extern int njobs; 102 | extern job_rec **job_array; 103 | 104 | extern int running_jobs,running_mailers; 105 | 106 | extern int complaints; 107 | 108 | extern time_t start_sec; 109 | 110 | /* time ranges for START_HOURS_RANGE */ 111 | extern int range_start; 112 | extern int range_stop; 113 | 114 | /* preferred hour for jobs */ 115 | extern int preferred_hour; 116 | 117 | /* Function prototypes */ 118 | 119 | /* main.c */ 120 | int xopen(int fd, const char *file_name, int flags); 121 | void xclose(int fd); 122 | pid_t xfork(void); 123 | 124 | #ifdef __GNUC__ 125 | #define PRINTF_FORMAT(n, m) \ 126 | __attribute__ ((format (printf, n, m))) 127 | #else 128 | #define PRINTF_FORMAT(n, m) 129 | #endif 130 | 131 | /* log.c */ 132 | void explain(const char *fmt, ...)PRINTF_FORMAT(1,2); 133 | void explain_e(const char *fmt, ...)PRINTF_FORMAT(1,2); 134 | void complain(const char *fmt, ...)PRINTF_FORMAT(1,2); 135 | void complain_e(const char *fmt, ...)PRINTF_FORMAT(1,2); 136 | void die(const char *fmt, ...)PRINTF_FORMAT(1,2) ATTRIBUTE_NORETURN; 137 | void die_e(const char *fmt, ...)PRINTF_FORMAT(1,2) ATTRIBUTE_NORETURN; 138 | void xdebug(const char *fmt, ...)PRINTF_FORMAT(1,2); 139 | void xdebug_e(const char *fmt, ...)PRINTF_FORMAT(1,2); 140 | void xcloselog(void); 141 | 142 | #ifdef DEBUG 143 | #define Debug(args) xdebug args 144 | #define Debug_e(args) xdebug_e args 145 | #else /* not DEBUG */ 146 | #define Debug(args) (void)(0) 147 | #define Debug_e(args) (void)(0) 148 | #endif /* not DEBUG */ 149 | 150 | /* readtab.c */ 151 | void read_tab(int cwd); 152 | void arrange_jobs(void); 153 | 154 | /* lock.c */ 155 | int consider_job(job_rec *jr); 156 | void unlock(job_rec *jr); 157 | void update_timestamp(job_rec *jr); 158 | void fake_job(job_rec *jr); 159 | 160 | /* runjob.c */ 161 | void tend_children(); 162 | void launch_job(job_rec *jr); 163 | 164 | #endif 165 | -------------------------------------------------------------------------------- /anacron/gregor.c: -------------------------------------------------------------------------------- 1 | /* 2 | Anacron - run commands periodically 3 | Copyright (C) 1998 Itai Tzur 4 | Copyright (C) 1999 Sean 'Shaleh' Perry 5 | Copyright (C) 2004 Pascal Hakim 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | The GNU General Public License can also be found in the file 22 | `COPYING' that comes with the Anacron source distribution. 23 | */ 24 | 25 | 26 | #include 27 | #include 28 | #include "gregor.h" 29 | 30 | static const int 31 | days_in_month[] = { 32 | 31, /* Jan */ 33 | 28, /* Feb (non-leap) */ 34 | 31, /* Mar */ 35 | 30, /* Apr */ 36 | 31, /* May */ 37 | 30, /* Jun */ 38 | 31, /* Jul */ 39 | 31, /* Aug */ 40 | 30, /* Sep */ 41 | 31, /* Oct */ 42 | 30, /* Nov */ 43 | 31 /* Dec */ 44 | }; 45 | 46 | static int leap(int year); 47 | 48 | int 49 | day_num(int year, int month, int day) 50 | /* Return the "day number" of the date year-month-day according to the 51 | * "proleptic Gregorian calendar". 52 | * If the given date is invalid, return -1. 53 | * 54 | * Here, "day number" is defined as the number of days since December 31, 55 | * 1 B.C. (Gregorian). (January 1, 1 A.D. is day number 1 etc...) 56 | * 57 | * The Gregorian calendar was instituted by Pope Gregory XIII in 1582, 58 | * and has gradually spread to become the international standard calendar. 59 | * The proleptic Gregorian calendar is formed by projecting the date system 60 | * of the Gregorian calendar to dates before its adoption. 61 | * 62 | * For more details, see: 63 | * http://astro.nmsu.edu/~lhuber/leaphist.html 64 | * http://www.magnet.ch/serendipity/hermetic/cal_stud/cal_art.htm 65 | * and your local library. 66 | */ 67 | { 68 | int dn; 69 | int i; 70 | int isleap; /* save three calls to leap() */ 71 | 72 | /* Some validity checks */ 73 | 74 | /* we don't deal with B.C. years here */ 75 | if (year < 1) return - 1; 76 | /* conservative overflow estimate */ 77 | if (year > (INT_MAX / 366)) return - 1; 78 | if (month > 12 || month < 1) return - 1; 79 | if (day < 1) return - 1; 80 | 81 | isleap = leap(year); 82 | 83 | if (month != 2) { 84 | if(day > days_in_month[month - 1]) return - 1; 85 | } 86 | else if ((isleap && day > 29) || (!isleap && day > 28)) 87 | return - 1; 88 | 89 | /* First calculate the day number of December 31 last year */ 90 | 91 | /* save us from doing (year - 1) over and over */ 92 | i = year - 1; 93 | /* 365 days in a "regular" year + number of leap days */ 94 | dn = (i * 365) + ((i / 4) - (i / 100) + (i / 400)); 95 | 96 | /* Now, day number of the last day of the previous month */ 97 | 98 | for (i = month - 1; i > 0; --i) 99 | dn += days_in_month[i - 1]; 100 | /* Add 29 February ? */ 101 | if (month > 2 && isleap) ++dn; 102 | 103 | /* How many days into month are we */ 104 | 105 | dn += day; 106 | 107 | return dn; 108 | } 109 | 110 | static int 111 | leap(int year) 112 | /* Is this a leap year ? */ 113 | { 114 | /* every year exactly divisible by 4 is "leap" */ 115 | /* unless it is exactly divisible by 100 */ 116 | /* but not by 400 */ 117 | return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); 118 | } 119 | 120 | int 121 | days_last_month (void) 122 | /* How many days did last month have? */ 123 | { 124 | struct tm time_record; 125 | time_t current_time; 126 | time (¤t_time); 127 | localtime_r (¤t_time, &time_record); 128 | 129 | switch (time_record.tm_mon) { 130 | case 0: return days_in_month[11]; 131 | case 2: return days_in_month[1] + (leap (time_record.tm_year + 1900) ? 1 : 0); 132 | default: return days_in_month[time_record.tm_mon - 1]; 133 | } 134 | } 135 | 136 | int 137 | days_this_month (void) 138 | /* How many days does this month have? */ 139 | { 140 | struct tm time_record; 141 | time_t current_time; 142 | time (¤t_time); 143 | localtime_r (¤t_time, &time_record); 144 | 145 | switch (time_record.tm_mon) { 146 | case 1: return days_in_month[1] + (leap (time_record.tm_year + 1900) ? 1 : 0); 147 | default: return days_in_month[time_record.tm_mon]; 148 | } 149 | } 150 | 151 | int 152 | days_last_year (void) 153 | /* How many days this last year have? */ 154 | { 155 | struct tm time_record; 156 | time_t current_time; 157 | time (¤t_time); 158 | localtime_r (¤t_time, &time_record); 159 | 160 | if (leap(time_record.tm_year - 1 + 1900)) { 161 | return 366; 162 | } 163 | 164 | return 365; 165 | } 166 | 167 | int 168 | days_this_year (void) 169 | /* How many days does this year have */ 170 | { 171 | struct tm time_record; 172 | time_t current_time; 173 | time (¤t_time); 174 | localtime_r (¤t_time, &time_record); 175 | 176 | if (leap(time_record.tm_year + 1900)) { 177 | return 366; 178 | } 179 | 180 | return 365; 181 | } 182 | -------------------------------------------------------------------------------- /anacron/gregor.h: -------------------------------------------------------------------------------- 1 | /* 2 | Anacron - run commands periodically 3 | Copyright (C) 1998 Itai Tzur 4 | Copyright (C) 1999 Sean 'Shaleh' Perry 5 | Copyright (C) 2004 Pascal Hakim 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | The GNU General Public License can also be found in the file 22 | `COPYING' that comes with the Anacron source distribution. 23 | */ 24 | 25 | 26 | int day_num(int year, int month, int day); 27 | int days_last_month (void); 28 | int days_this_month (void); 29 | int days_last_year (void); 30 | int days_this_year (void); 31 | -------------------------------------------------------------------------------- /anacron/lock.c: -------------------------------------------------------------------------------- 1 | /* 2 | Anacron - run commands periodically 3 | Copyright (C) 1998 Itai Tzur 4 | Copyright (C) 1999 Sean 'Shaleh' Perry 5 | Copyright (C) 2004 Pascal Hakim 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | The GNU General Public License can also be found in the file 22 | `COPYING' that comes with the Anacron source distribution. 23 | */ 24 | 25 | 26 | /* Lock and timestamp management 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "global.h" 37 | #include "gregor.h" 38 | 39 | static void 40 | open_tsfile(job_rec *jr) 41 | /* Open the timestamp file for job jr */ 42 | { 43 | jr->timestamp_fd = open(jr->ident, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); 44 | if (jr->timestamp_fd == -1) 45 | die_e("Can't open timestamp file for job %s", jr->ident); 46 | fcntl(jr->timestamp_fd, F_SETFD, 1); /* set close-on-exec flag */ 47 | /* We want to own this file, and set its mode to 0600. This is necessary 48 | * in order to prevent other users from putting locks on it. */ 49 | if (fchown(jr->timestamp_fd, getuid(), getgid())) 50 | die_e("Can't chown timestamp file %s", jr->ident); 51 | if (fchmod(jr->timestamp_fd, S_IRUSR | S_IWUSR)) 52 | die_e("Can't chmod timestamp file %s", jr->ident); 53 | } 54 | 55 | static int 56 | lock_file(int fd) 57 | /* Attempt to put an exclusive fcntl() lock on file "fd" 58 | * Return 1 on success, 0 on failure. 59 | */ 60 | { 61 | int r; 62 | struct flock sfl; 63 | 64 | sfl.l_type = F_WRLCK; 65 | sfl.l_start = 0; 66 | sfl.l_whence = SEEK_SET; 67 | sfl.l_len = 0; /* we lock all the file */ 68 | errno = 0; 69 | r = fcntl(fd, F_SETLK, &sfl); 70 | if (r != -1) return 1; 71 | if (errno != EACCES && errno != EAGAIN) 72 | die_e("fcntl() error"); 73 | return 0; 74 | } 75 | 76 | int 77 | consider_job(job_rec *jr) 78 | /* Check the timestamp of the job. If "its time has come", lock the job 79 | * and return 1, if it's too early, or we can't get the lock, return 0. 80 | */ 81 | { 82 | char timestamp[9]; 83 | int ts_year, ts_month, ts_day, dn; 84 | ssize_t b; 85 | 86 | open_tsfile(jr); 87 | 88 | /* read timestamp */ 89 | b = read(jr->timestamp_fd, timestamp, 8); 90 | if (b == -1) die_e("Error reading timestamp file %s", jr->ident); 91 | timestamp[8] = 0; 92 | 93 | /* is it too early? */ 94 | if (!force && b == 8) 95 | { 96 | int day_delta; 97 | time_t jobtime; 98 | struct tm *t; 99 | 100 | if (sscanf(timestamp, "%4d%2d%2d", &ts_year, &ts_month, &ts_day) == 3) 101 | dn = day_num(ts_year, ts_month, ts_day); 102 | else 103 | dn = 0; 104 | 105 | day_delta = day_now - dn; 106 | 107 | /* 108 | * if day_delta is negative, we assume there was a clock skew 109 | * and re-run any affected jobs 110 | * otherwise we check if the job's time has come 111 | */ 112 | if (day_delta >= 0 && day_delta < jr->period) 113 | { 114 | /* yes, skip job */ 115 | xclose(jr->timestamp_fd); 116 | return 0; 117 | } 118 | 119 | /* 120 | * Check to see if it's a named period, in which case we need 121 | * to figure it out. 122 | */ 123 | if (jr->named_period) 124 | { 125 | int period = 0, bypass = 0; 126 | switch (jr->named_period) 127 | { 128 | case 1: /* monthly */ 129 | period = days_last_month (); 130 | bypass = days_this_month (); 131 | break; 132 | case 2: /* yearly, annually */ 133 | period = days_last_year (); 134 | bypass = days_this_year (); 135 | break; 136 | case 3: /* daily */ 137 | period = 1; 138 | bypass = 1; 139 | break; 140 | case 4: /* weekly */ 141 | period = 7; 142 | bypass = 7; 143 | break; 144 | default: 145 | die ("Unknown named period for %s (%d)", jr->ident, jr->named_period); 146 | } 147 | printf ("Checking against %d with %d\n", day_delta, period); 148 | if (day_delta < period && day_delta != bypass) 149 | { 150 | /* Job is still too young */ 151 | xclose (jr->timestamp_fd); 152 | return 0; 153 | } 154 | } 155 | 156 | jobtime = start_sec + jr->delay * 60; 157 | 158 | t = localtime(&jobtime); 159 | if (!now && preferred_hour != -1 && t->tm_hour != preferred_hour) { 160 | Debug(("The job's %s preferred hour %d was missed, skipping the job.", jr->ident, preferred_hour)); 161 | xclose (jr->timestamp_fd); 162 | return 0; 163 | } 164 | 165 | if (!now && range_start != -1 && range_stop != -1 && 166 | (t->tm_hour < range_start || t->tm_hour >= range_stop)) 167 | { 168 | Debug(("The job `%s' falls out of the %02d:00-%02d:00 hours range, skipping.", 169 | jr->ident, range_start, range_stop)); 170 | xclose (jr->timestamp_fd); 171 | return 0; 172 | } 173 | } 174 | 175 | /* no! try to grab the lock */ 176 | if (lock_file(jr->timestamp_fd)) return 1; /* success */ 177 | 178 | /* didn't get lock */ 179 | xclose(jr->timestamp_fd); 180 | explain("Job `%s' locked by another anacron - skipping", jr->ident); 181 | return 0; 182 | } 183 | 184 | void 185 | unlock(job_rec *jr) 186 | { 187 | xclose(jr->timestamp_fd); 188 | } 189 | 190 | void 191 | update_timestamp(job_rec *jr) 192 | /* We write the date "now". "Now" can be either the time when anacron 193 | * started, or the time when the job finished. 194 | * I'm not quite sure which is more "right", but I've decided on the first 195 | * option. 196 | * Note that this is not the way it was with anacron 1.0.3 to 1.0.7. 197 | */ 198 | { 199 | char stamp[10]; 200 | 201 | snprintf(stamp, 10, "%04d%02d%02d\n", year, month, day_of_month); 202 | if (lseek(jr->timestamp_fd, 0, SEEK_SET)) 203 | die_e("Can't lseek timestamp file for job %s", jr->ident); 204 | if (write(jr->timestamp_fd, stamp, 9) != 9) 205 | die_e("Can't write timestamp file for job %s", jr->ident); 206 | if (ftruncate(jr->timestamp_fd, 9)) 207 | die_e("ftruncate error"); 208 | } 209 | 210 | void 211 | fake_job(job_rec *jr) 212 | /* We don't bother with any locking here. There's no point. */ 213 | { 214 | open_tsfile(jr); 215 | update_timestamp(jr); 216 | xclose(jr->timestamp_fd); 217 | } 218 | -------------------------------------------------------------------------------- /anacron/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | Anacron - run commands periodically 3 | Copyright (C) 1998 Itai Tzur 4 | Copyright (C) 1999 Sean 'Shaleh' Perry 5 | Copyright (C) 2004 Pascal Hakim 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | The GNU General Public License can also be found in the file 22 | `COPYING' that comes with the Anacron source distribution. 23 | */ 24 | 25 | 26 | /* Error logging 27 | * 28 | * We have two levels of logging (plus debugging if DEBUG is defined): 29 | * "explain" level for informational messages, and "complain" level for errors. 30 | * 31 | * We log everything to syslog, see the top of global.h for relevant 32 | * definitions. 33 | * 34 | * Stderr gets "complain" messages when we're in the foreground, 35 | * and "explain" messages when we're in the foreground, and not "quiet". 36 | */ 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include "global.h" 48 | 49 | static char truncated[] = " (truncated)"; 50 | static char msg[MAX_MSG + 1]; 51 | static int log_open = 0; 52 | 53 | /* Number of complaints that we've seen */ 54 | int complaints = 0; 55 | 56 | static void 57 | xopenlog(void) 58 | { 59 | if (!log_open) 60 | { 61 | openlog(program_name, LOG_PID, SYSLOG_FACILITY); 62 | log_open = 1; 63 | } 64 | } 65 | 66 | void 67 | xcloselog(void) 68 | { 69 | if (log_open) closelog(); 70 | log_open = 0; 71 | } 72 | 73 | static void 74 | make_msg(const char *fmt, va_list args) 75 | /* Construct the message string from its parts */ 76 | { 77 | int len; 78 | 79 | /* There's some confusion in the documentation about what vsnprintf 80 | * returns when the buffer overflows. Hmmm... */ 81 | len = vsnprintf(msg, sizeof(msg), fmt, args); 82 | if (len < 0) { 83 | strncpy(msg, "(vsnprintf failed)", sizeof(msg)); 84 | msg[sizeof(msg) - 1] = '\0'; 85 | return; 86 | } 87 | if ((size_t) len >= sizeof(msg) - 1) 88 | strcpy(msg + sizeof(msg) - sizeof(truncated), truncated); 89 | } 90 | 91 | static void 92 | slog(int priority, const char *fmt, va_list args) 93 | /* Log a message, described by "fmt" and "args", with the specified 94 | * "priority". */ 95 | { 96 | make_msg(fmt, args); 97 | xopenlog(); 98 | syslog(priority, "%s", msg); 99 | if (!in_background) 100 | { 101 | if (priority == EXPLAIN_LEVEL && !quiet) 102 | fprintf(stderr, "%s\n", msg); 103 | else if (priority == COMPLAIN_LEVEL) 104 | fprintf(stderr, "%s: %s\n", program_name, msg); 105 | } 106 | } 107 | 108 | static void 109 | log_e(int priority, const char *fmt, va_list args) 110 | /* Same as slog(), but also appends an error description corresponding 111 | * to "errno". */ 112 | { 113 | int saved_errno; 114 | 115 | saved_errno = errno; 116 | make_msg(fmt, args); 117 | xopenlog(); 118 | syslog(priority, "%s: %s", msg, strerror(saved_errno)); 119 | if (!in_background) 120 | { 121 | if (priority == EXPLAIN_LEVEL && !quiet) 122 | fprintf(stderr, "%s: %s\n", msg, strerror(saved_errno)); 123 | else if (priority == COMPLAIN_LEVEL) 124 | fprintf(stderr, "%s: %s: %s\n", 125 | program_name, msg, strerror(saved_errno)); 126 | } 127 | } 128 | 129 | void 130 | explain(const char *fmt, ...) 131 | /* Log an "explain" level message */ 132 | { 133 | va_list args; 134 | 135 | va_start(args, fmt); 136 | slog(EXPLAIN_LEVEL, fmt, args); 137 | va_end(args); 138 | } 139 | 140 | void 141 | explain_e(const char *fmt, ...) 142 | /* Log an "explain" level message, with an error description */ 143 | { 144 | va_list args; 145 | 146 | va_start(args, fmt); 147 | log_e(EXPLAIN_LEVEL, fmt, args); 148 | va_end(args); 149 | } 150 | 151 | void 152 | complain(const char *fmt, ...) 153 | /* Log a "complain" level message */ 154 | { 155 | va_list args; 156 | 157 | va_start(args, fmt); 158 | slog(COMPLAIN_LEVEL, fmt, args); 159 | va_end(args); 160 | 161 | complaints += 1; 162 | } 163 | 164 | void 165 | complain_e(const char *fmt, ...) 166 | /* Log a "complain" level message, with an error description */ 167 | { 168 | va_list args; 169 | 170 | va_start(args, fmt); 171 | log_e(COMPLAIN_LEVEL, fmt, args); 172 | va_end(args); 173 | 174 | complaints += 1; 175 | } 176 | 177 | void 178 | die(const char *fmt, ...) 179 | /* Log a "complain" level message, and exit */ 180 | { 181 | va_list args; 182 | 183 | va_start(args, fmt); 184 | slog(COMPLAIN_LEVEL, fmt, args); 185 | va_end(args); 186 | if (getpid() == primary_pid) complain("Aborted"); 187 | 188 | exit(FAILURE_EXIT); 189 | } 190 | 191 | void 192 | die_e(const char *fmt, ...) 193 | /* Log a "complain" level message, with an error description, and exit */ 194 | { 195 | va_list args; 196 | 197 | va_start(args, fmt); 198 | log_e(COMPLAIN_LEVEL, fmt, args); 199 | va_end(args); 200 | if (getpid() == primary_pid) complain("Aborted"); 201 | 202 | exit(FAILURE_EXIT); 203 | } 204 | 205 | #ifdef DEBUG 206 | 207 | /* These are called through the Debug() and Debug_e() macros, defined 208 | * in global.h */ 209 | 210 | void 211 | xdebug(const char *fmt, ...) 212 | { 213 | va_list args; 214 | 215 | va_start(args, fmt); 216 | slog(DEBUG_LEVEL, fmt, args); 217 | va_end(args); 218 | } 219 | 220 | void 221 | xdebug_e(const char *fmt, ...) 222 | { 223 | va_list args; 224 | 225 | va_start(args, fmt); 226 | log_e(DEBUG_LEVEL, fmt, args); 227 | va_end(args); 228 | } 229 | 230 | #endif /* DEBUG */ 231 | -------------------------------------------------------------------------------- /anacron/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Anacron - run commands periodically 3 | Copyright (C) 1998 Itai Tzur 4 | Copyright (C) 1999 Sean 'Shaleh' Perry 5 | Copyright (C) 2004 Pascal Hakim 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | The GNU General Public License can also be found in the file 22 | `COPYING' that comes with the Anacron source distribution. 23 | */ 24 | 25 | #include "config.h" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "global.h" 39 | #include "gregor.h" 40 | #include "cronie_common.h" 41 | 42 | pid_t primary_pid; 43 | int day_now; 44 | int year, month, day_of_month; /* date anacron started */ 45 | 46 | char *program_name; 47 | char *anacrontab = NULL; 48 | char *spooldir = NULL; 49 | int serialize, force, update_only, now, 50 | no_daemon, quiet, testing_only; /* command-line options */ 51 | char **job_args; /* vector of "job" command-line arguments */ 52 | int job_nargs; /* number of these */ 53 | char *defarg = "*"; 54 | int in_background; /* are we in the background? */ 55 | sigset_t old_sigmask; /* signal mask when started */ 56 | 57 | job_rec *first_job_rec; 58 | env_rec *first_env_rec; 59 | 60 | time_t start_sec; /* time anacron started */ 61 | static volatile int got_sigalrm, got_sigchld, got_sigusr1; 62 | int running_jobs, running_mailers; /* , number of */ 63 | int range_start = -1; 64 | int range_stop = -1; 65 | int preferred_hour = -1; 66 | 67 | static void 68 | print_version(void) 69 | { 70 | printf("Anacron from project %s\n" 71 | "Copyright (C) 1998 Itai Tzur \n" 72 | "Copyright (C) 1999 Sean 'Shaleh' Perry \n" 73 | "Copyright (C) 2004 Pascal Hakim \n" 74 | "\n" 75 | "Mail comments, suggestions and bug reports to ." 76 | "\n\n", PACKAGE_STRING); 77 | } 78 | 79 | static void 80 | print_usage(void) 81 | { 82 | printf("Usage:\n"); 83 | printf(" %s [options] [job] ...\n", program_name); 84 | printf(" %s -T [-t anacrontab-file]\n", program_name); 85 | printf("\nOptions:\n"); 86 | printf(" -s Serialize execution of jobs\n"); 87 | printf(" -f Force execution of jobs, even before their time\n"); 88 | printf(" -n Run jobs with no delay, implies -s\n"); 89 | printf(" -d Don't fork to the background\n"); 90 | printf(" -q Suppress stderr messages, only applicable with -d\n"); 91 | printf(" -u Update the timestamps without actually running anything\n"); 92 | printf(" -V Print version information\n"); 93 | printf(" -h Print this message\n"); 94 | printf(" -t Use alternative anacrontab\n"); 95 | printf(" -T Test an anacrontab\n"); 96 | printf(" -S Select a different spool directory\n"); 97 | printf("\nSee the anacron(8) manpage for more details.\n"); 98 | } 99 | 100 | static void 101 | parse_opts(int argc, char *argv[]) 102 | /* Parse command-line options */ 103 | { 104 | int opt; 105 | 106 | quiet = no_daemon = serialize = force = update_only = now = 0; 107 | opterr = 0; 108 | while ((opt = getopt(argc, argv, "sfundqt:TS:Vh")) != EOF) 109 | { 110 | switch (opt) 111 | { 112 | case 's': 113 | serialize = 1; 114 | break; 115 | case 'f': 116 | force = 1; 117 | break; 118 | case 'u': 119 | update_only = 1; 120 | break; 121 | case 'n': 122 | now = serialize = 1; 123 | break; 124 | case 'd': 125 | no_daemon = 1; 126 | break; 127 | case 'q': 128 | quiet = 1; 129 | break; 130 | case 't': 131 | free(anacrontab); 132 | anacrontab = strdup(optarg); 133 | break; 134 | case 'T': 135 | testing_only = 1; 136 | break; 137 | case 'S': 138 | free(spooldir); 139 | spooldir = strdup(optarg); 140 | break; 141 | case 'V': 142 | print_version(); 143 | exit(EXIT_SUCCESS); 144 | case 'h': 145 | print_usage(); 146 | exit(EXIT_SUCCESS); 147 | case '?': 148 | fprintf(stderr, "%s: invalid option: %c\n", 149 | program_name, optopt); 150 | fprintf(stderr, "type: `%s -h' for more information\n", 151 | program_name); 152 | exit(FAILURE_EXIT); 153 | } 154 | } 155 | if (optind == argc) 156 | { 157 | /* no arguments. Equivalent to: `*' */ 158 | job_nargs = 1; 159 | job_args = &defarg; 160 | } 161 | else 162 | { 163 | job_nargs = argc - optind; 164 | job_args = argv + optind; 165 | } 166 | } 167 | 168 | pid_t 169 | xfork(void) 170 | /* Like fork(), only never returns on failure */ 171 | { 172 | pid_t pid; 173 | 174 | pid = fork(); 175 | if (pid == -1) die_e("Can't fork"); 176 | return pid; 177 | } 178 | 179 | int 180 | xopen(int fd, const char *file_name, int flags) 181 | /* Like open, only it: 182 | * a) never returns on failure, and 183 | * b) if "fd" is non-negative, expect the file to open 184 | * on file-descriptor "fd". 185 | */ 186 | { 187 | int rfd; 188 | 189 | rfd = open(file_name, flags); 190 | if (fd >= 0 && rfd != fd) 191 | die_e("Can't open %s on file-descriptor %d", file_name, fd); 192 | else if (rfd < 0) 193 | die_e("Can't open %s", file_name); 194 | return rfd; 195 | } 196 | 197 | void 198 | xclose(int fd) 199 | /* Like close(), only doesn't return on failure */ 200 | { 201 | if (close(fd)) die_e("Can't close file descriptor %d", fd); 202 | } 203 | 204 | static void 205 | go_background(void) 206 | /* Become a daemon. The foreground process exits successfully. */ 207 | { 208 | pid_t pid; 209 | 210 | /* stdin is already closed */ 211 | 212 | xclose(STDOUT_FILENO); 213 | /* coverity[leaked_handle] – fd 1 closed automatically */ 214 | xopen(STDOUT_FILENO, "/dev/null", O_WRONLY); 215 | 216 | xclose(STDERR_FILENO); 217 | /* coverity[leaked_handle] – fd 2 closed automatically */ 218 | xopen(STDERR_FILENO, "/dev/null", O_WRONLY); 219 | 220 | pid = xfork(); 221 | if (pid != 0) 222 | { 223 | /* parent */ 224 | exit(EXIT_SUCCESS); 225 | } 226 | else 227 | { 228 | /* child */ 229 | primary_pid = getpid(); 230 | if (setsid() == -1) die_e("setsid() error"); 231 | in_background = 1; 232 | } 233 | } 234 | 235 | static void 236 | handle_sigalrm(int unused ATTRIBUTE_UNUSED) 237 | { 238 | got_sigalrm = 1; 239 | } 240 | 241 | static void 242 | handle_sigchld(int unused ATTRIBUTE_UNUSED) 243 | { 244 | got_sigchld = 1; 245 | } 246 | 247 | static void 248 | handle_sigusr1(int unused ATTRIBUTE_UNUSED) 249 | { 250 | got_sigusr1 = 1; 251 | } 252 | 253 | static void 254 | set_signal_handling(void) 255 | /* We only use SIGALRM, SIGCHLD and SIGUSR1, and we unblock them only 256 | * in wait_signal(). 257 | */ 258 | { 259 | sigset_t ss; 260 | struct sigaction sa; 261 | 262 | got_sigalrm = got_sigchld = got_sigusr1 = 0; 263 | 264 | /* block SIGALRM, SIGCHLD and SIGUSR1 */ 265 | if (sigemptyset(&ss) || 266 | sigaddset(&ss, SIGALRM) || 267 | sigaddset(&ss, SIGCHLD) || 268 | sigaddset(&ss, SIGUSR1)) die_e("sigset error"); 269 | if (sigprocmask(SIG_BLOCK, &ss, NULL)) die_e ("sigprocmask error"); 270 | 271 | /* setup SIGALRM handler */ 272 | sa.sa_handler = handle_sigalrm; 273 | sa.sa_mask = ss; 274 | sa.sa_flags = 0; 275 | if (sigaction(SIGALRM, &sa, NULL)) die_e("sigaction error"); 276 | 277 | /* setup SIGCHLD handler */ 278 | sa.sa_handler = handle_sigchld; 279 | sa.sa_mask = ss; 280 | sa.sa_flags = SA_NOCLDSTOP; 281 | if (sigaction(SIGCHLD, &sa, NULL)) die_e("sigaction error"); 282 | 283 | /* setup SIGUSR1 handler */ 284 | sa.sa_handler = handle_sigusr1; 285 | sa.sa_mask = ss; 286 | sa.sa_flags = 0; 287 | if (sigaction(SIGUSR1, &sa, NULL)) die_e("sigaction error"); 288 | } 289 | 290 | static void 291 | wait_signal(void) 292 | /* Return after a signal is caught */ 293 | { 294 | sigset_t ss; 295 | 296 | if (sigprocmask(0, NULL, &ss)) die_e("sigprocmask error"); 297 | if (sigdelset(&ss, SIGALRM) || 298 | sigdelset(&ss, SIGCHLD) || 299 | sigdelset(&ss, SIGUSR1)) die_e("sigset error"); 300 | sigsuspend(&ss); 301 | } 302 | 303 | static void 304 | wait_children(void) 305 | /* Wait until we have no more children (of any kind) */ 306 | { 307 | while (running_jobs > 0 || running_mailers > 0) 308 | { 309 | wait_signal(); 310 | if (got_sigchld) tend_children(); 311 | got_sigchld = 0; 312 | if (got_sigusr1) explain("Received SIGUSR1"); 313 | got_sigusr1 = 0; 314 | } 315 | } 316 | 317 | static void 318 | orderly_termination(void) 319 | /* Execution is diverted here, when we get SIGUSR1 */ 320 | { 321 | explain("Received SIGUSR1"); 322 | got_sigusr1 = 0; 323 | wait_children(); 324 | explain("Exited"); 325 | exit(EXIT_SUCCESS); 326 | } 327 | 328 | static void 329 | xsleep(unsigned int n) 330 | /* Sleep for n seconds, servicing SIGCHLDs and SIGUSR1s in the meantime. 331 | * If n=0, return immediately. 332 | */ 333 | { 334 | if (n == 0) return; 335 | alarm(n); 336 | do 337 | { 338 | wait_signal(); 339 | if (got_sigchld) tend_children(); 340 | got_sigchld = 0; 341 | if (got_sigusr1) orderly_termination(); 342 | } 343 | while (!got_sigalrm); 344 | got_sigalrm = 0; 345 | } 346 | 347 | static void 348 | wait_jobs(void) 349 | /* Wait until there are no running jobs, 350 | * servicing SIGCHLDs and SIGUSR1s in the meantime. 351 | */ 352 | { 353 | while (running_jobs > 0) 354 | { 355 | wait_signal(); 356 | if (got_sigchld) tend_children(); 357 | got_sigchld = 0; 358 | if (got_sigusr1) orderly_termination(); 359 | } 360 | } 361 | 362 | static void 363 | record_start_time(void) 364 | { 365 | struct tm *tm_now; 366 | 367 | start_sec = time(NULL); 368 | tm_now = localtime(&start_sec); 369 | year = tm_now->tm_year + 1900; 370 | month = tm_now->tm_mon + 1; 371 | day_of_month = tm_now->tm_mday; 372 | day_now = day_num(year, month, day_of_month); 373 | if (day_now == -1) die("Invalid date (this is really embarrassing)"); 374 | if (!update_only && !testing_only) 375 | explain("Anacron started on %04d-%02d-%02d", 376 | year, month, day_of_month); 377 | } 378 | 379 | static unsigned int 380 | time_till(job_rec *jr) 381 | /* Return the number of seconds that we have to wait until it's time 382 | * to start job jr. 383 | */ 384 | { 385 | time_t tj, tn; 386 | 387 | if (now) return 0; 388 | tn = time(NULL); 389 | tj = start_sec + (time_t)jr->delay * 60; 390 | if (tj < tn) return 0; 391 | if (tj - tn > 3600*24) 392 | { 393 | explain("System time manipulation detected, job `%s' will run immediately", 394 | jr->ident); 395 | return 0; 396 | } 397 | return (unsigned int)(tj - tn); 398 | } 399 | 400 | static void 401 | fake_jobs(void) 402 | { 403 | int j; 404 | 405 | j = 0; 406 | while (j < njobs) 407 | { 408 | fake_job(job_array[j]); 409 | explain("Updated timestamp for job `%s' to %04d-%02d-%02d", 410 | job_array[j]->ident, year, month, day_of_month); 411 | j++; 412 | } 413 | } 414 | 415 | static void 416 | explain_intentions(void) 417 | { 418 | int j; 419 | 420 | j = 0; 421 | while (j < njobs) 422 | { 423 | if (now) 424 | { 425 | explain("Will run job `%s'", job_array[j]->ident); 426 | } 427 | else 428 | { 429 | explain("Will run job `%s' in %d min.", 430 | job_array[j]->ident, job_array[j]->delay); 431 | } 432 | j++; 433 | } 434 | if (serialize && njobs > 0) 435 | explain("Jobs will be executed sequentially"); 436 | } 437 | 438 | int 439 | main(int argc, char *argv[]) 440 | { 441 | int j; 442 | int cwd; 443 | struct timeval tv; 444 | struct timezone tz; 445 | 446 | anacrontab = NULL; 447 | spooldir = NULL; 448 | 449 | setlocale(LC_ALL, ""); 450 | 451 | if (gettimeofday(&tv, &tz) != 0) 452 | explain("Can't get exact time, failure."); 453 | 454 | srandom((unsigned int)(getpid() + tv.tv_usec)); 455 | 456 | if((program_name = strrchr(argv[0], '/')) == NULL) 457 | program_name = argv[0]; 458 | else 459 | ++program_name; /* move pointer to char after '/' */ 460 | 461 | parse_opts(argc, argv); 462 | 463 | if (anacrontab == NULL) 464 | anacrontab = strdup(ANACRONTAB); 465 | 466 | if (spooldir == NULL) 467 | spooldir = strdup(ANACRON_SPOOL_DIR); 468 | 469 | if ((cwd = open ("./", O_RDONLY)) == -1) { 470 | die_e ("Can't save current directory"); 471 | } 472 | 473 | in_background = 0; 474 | 475 | if (chdir(spooldir)) die_e("Can't chdir to %s", spooldir ); 476 | 477 | if (sigprocmask(0, NULL, &old_sigmask)) die_e("sigset error"); 478 | 479 | xclose(STDIN_FILENO); 480 | xopen(STDIN_FILENO, "/dev/null", O_RDONLY); 481 | 482 | if (!no_daemon && !testing_only) 483 | go_background(); 484 | else 485 | primary_pid = getpid(); 486 | 487 | record_start_time(); 488 | read_tab(cwd); 489 | close(cwd); 490 | arrange_jobs(); 491 | 492 | if (testing_only) 493 | { 494 | if (complaints) exit (EXIT_FAILURE); 495 | 496 | exit (EXIT_SUCCESS); 497 | } 498 | 499 | if (update_only) 500 | { 501 | fake_jobs(); 502 | exit(EXIT_SUCCESS); 503 | } 504 | 505 | explain_intentions(); 506 | set_signal_handling(); 507 | running_jobs = running_mailers = 0; 508 | for(j = 0; j < njobs; ++j) 509 | { 510 | xsleep(time_till(job_array[j])); 511 | if (serialize) wait_jobs(); 512 | launch_job(job_array[j]); 513 | } 514 | wait_children(); 515 | explain("Normal exit (%d job%s run)", njobs, njobs == 1 ? "" : "s"); 516 | exit(EXIT_SUCCESS); 517 | } 518 | -------------------------------------------------------------------------------- /anacron/matchrx.c: -------------------------------------------------------------------------------- 1 | /* 2 | Anacron - run commands periodically 3 | Copyright (C) 1998 Itai Tzur 4 | Copyright (C) 1999 Sean 'Shaleh' Perry 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License along 17 | with this program; if not, write to the Free Software Foundation, Inc., 18 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | The GNU General Public License can also be found in the file 21 | `COPYING' that comes with the Anacron source distribution. 22 | */ 23 | 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "matchrx.h" 31 | 32 | int 33 | match_rx(const char *rx, char *string, unsigned int n_sub, /* char **substrings */...) 34 | /* Return 1 if the regular expression "*rx" matches the string "*string", 35 | * 0 if not, -1 on error. 36 | * "Extended" regular expressions are used. 37 | * Additionally, there should be "n_sub" "substrings" arguments. These, 38 | * if not NULL, and if the match succeeds are set to point to the 39 | * corresponding substrings of the regexp. 40 | * The original string is changed, and the substrings must not overlap, 41 | * or even be directly adjacent. 42 | * This is not the most efficient, or elegant way of doing this. 43 | */ 44 | { 45 | int r; 46 | unsigned int n; 47 | regex_t crx; 48 | va_list va; 49 | char **substring; 50 | regmatch_t *sub_offsets; 51 | 52 | sub_offsets = malloc(sizeof(regmatch_t) * (n_sub + 1)); 53 | if (sub_offsets == NULL) 54 | return -1; 55 | memset(sub_offsets, 0, sizeof(regmatch_t) * (n_sub + 1)); 56 | 57 | if (regcomp(&crx, rx, REG_EXTENDED)) { 58 | free(sub_offsets); 59 | return -1; 60 | } 61 | r = regexec(&crx, string, n_sub + 1, sub_offsets, 0); 62 | if (r != 0 && r != REG_NOMATCH) { 63 | free(sub_offsets); 64 | return -1; 65 | } 66 | regfree(&crx); 67 | if (r == REG_NOMATCH) { 68 | free(sub_offsets); 69 | return 0; 70 | } 71 | 72 | va_start(va, n_sub); 73 | n = 1; 74 | while (n <= n_sub) 75 | { 76 | substring = va_arg(va, char**); 77 | if (substring != NULL) 78 | { 79 | if (sub_offsets[n].rm_so == -1) { 80 | va_end(va); 81 | free(sub_offsets); 82 | return - 1; 83 | } 84 | *substring = string + sub_offsets[n].rm_so; 85 | *(string + sub_offsets[n].rm_eo) = 0; 86 | } 87 | n++; 88 | } 89 | va_end(va); 90 | free(sub_offsets); 91 | return 1; 92 | } 93 | -------------------------------------------------------------------------------- /anacron/matchrx.h: -------------------------------------------------------------------------------- 1 | /* 2 | Anacron - run commands periodically 3 | Copyright (C) 1998 Itai Tzur 4 | Copyright (C) 1999 Sean 'Shaleh' Perry 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License along 17 | with this program; if not, write to the Free Software Foundation, Inc., 18 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | The GNU General Public License can also be found in the file 21 | `COPYING' that comes with the Anacron source distribution. 22 | */ 23 | 24 | 25 | int match_rx(const char *rx, char *string, 26 | unsigned int n_sub, /* char **substrings */...); 27 | -------------------------------------------------------------------------------- /anacron/readtab.c: -------------------------------------------------------------------------------- 1 | /* 2 | Anacron - run commands periodically 3 | Copyright (C) 1998 Itai Tzur 4 | Copyright (C) 1999 Sean 'Shaleh' Perry 5 | Copyright (C) 2004 Pascal Hakim 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License along 18 | with this program; if not, write to the Free Software Foundation, Inc., 19 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 20 | 21 | The GNU General Public License can also be found in the file 22 | `COPYING' that comes with the Anacron source distribution. 23 | */ 24 | 25 | 26 | /* /etc/anacrontab parsing, and job sorting 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include "global.h" 39 | #include "matchrx.h" 40 | 41 | static struct obstack input_o; /* holds input line */ 42 | static struct obstack tab_o; /* holds processed data read from anacrontab */ 43 | static FILE *tab; 44 | job_rec **job_array; 45 | int njobs; /* number of jobs to run */ 46 | static int jobs_read; /* number of jobs read */ 47 | static int line_num; /* current line in anacrontab */ 48 | static job_rec *last_job_rec; /* last job stored in memory, at the moment */ 49 | static env_rec *last_env_rec; /* last environment assignment stored */ 50 | 51 | static int random_number = 0; 52 | 53 | /* some definitions for the obstack macros */ 54 | #define obstack_chunk_alloc xmalloc 55 | #define obstack_chunk_free free 56 | 57 | static void * 58 | xmalloc (size_t size) 59 | /* Just like standard malloc(), only never returns NULL. */ 60 | { 61 | void * ptr; 62 | 63 | ptr = malloc(size); 64 | if (ptr == NULL) 65 | die("Memory exhausted"); 66 | return ptr; 67 | } 68 | 69 | static int 70 | conv2int(const char *s) 71 | /* Return the int or -1 on over/under-flow 72 | */ 73 | { 74 | long l; 75 | 76 | errno = 0; 77 | l = strtol(s, NULL, 10); 78 | /* we use negative as error, so I am really returning unsigned int */ 79 | if (errno == ERANGE || l < 0 || l > INT_MAX) return - 1; 80 | return (int)l; 81 | } 82 | 83 | static char * 84 | read_tab_line (void) 85 | /* Read one line and return a pointer to it. 86 | Return NULL if no more lines. 87 | */ 88 | { 89 | int c, prev=0; 90 | 91 | if (feof(tab)) return NULL; 92 | while (1) 93 | { 94 | c = getc(tab); 95 | if ((c == '\n' && prev != '\\') || c == EOF) 96 | { 97 | if (0 != prev) obstack_1grow(&input_o, (char)prev); 98 | break; 99 | } 100 | 101 | if (('\\' != prev || c != '\n') && 0 != prev && '\n' != prev) obstack_1grow(&input_o, (char)prev); 102 | else if ('\n' == prev) obstack_1grow(&input_o, ' '); 103 | 104 | prev = c; 105 | } 106 | if (ferror(tab)) die_e("Error reading %s", anacrontab); 107 | obstack_1grow(&input_o, '\0'); 108 | return obstack_finish(&input_o); 109 | } 110 | 111 | static int 112 | job_arg_num(const char *ident) 113 | /* Return the command-line-argument number referring to this job-identifier. 114 | * If it isn't specified, return -1. 115 | */ 116 | { 117 | int i, r; 118 | 119 | for (i = 0; i < job_nargs; i++) 120 | { 121 | r = fnmatch(job_args[i], ident, 0); 122 | if (r == 0) return i; 123 | if (r != FNM_NOMATCH) die("fnmatch() error"); 124 | } 125 | return - 1; 126 | } 127 | 128 | static void 129 | register_env(const char *env_var, const char *value) 130 | /* Store the environment assignment "env_var"="value" */ 131 | { 132 | env_rec *er; 133 | int var_len, val_len; 134 | 135 | var_len = (int)strlen(env_var); 136 | val_len = (int)strlen(value); 137 | if (!var_len) { 138 | return; 139 | } 140 | 141 | er = obstack_alloc(&tab_o, sizeof(env_rec)); 142 | if (er == NULL) { 143 | die_e("Cannot allocate memory."); 144 | } 145 | 146 | er->assign = obstack_alloc(&tab_o, var_len + 1 + val_len + 1); 147 | if (er->assign == NULL) { 148 | die_e("Cannot allocate memory."); 149 | } 150 | strcpy(er->assign, env_var); 151 | er->assign[var_len] = '='; 152 | strcpy(er->assign + var_len + 1, value); 153 | er->assign[var_len + 1 + val_len] = 0; 154 | if (last_env_rec != NULL) last_env_rec->next = er; 155 | else first_env_rec = er; 156 | last_env_rec = er; 157 | Debug(("on line %d: %s", line_num, er->assign)); 158 | } 159 | 160 | static void 161 | register_job(const char *periods, const char *delays, 162 | const char *ident, char *command) 163 | /* Store a job definition */ 164 | { 165 | int period, delay; 166 | job_rec *jr; 167 | int ident_len, command_len; 168 | 169 | ident_len = (int)strlen(ident); 170 | command_len = (int)strlen(command); 171 | jobs_read++; 172 | period = conv2int(periods); 173 | delay = conv2int(delays); 174 | if (period < 0 || delay < 0) 175 | { 176 | complain("%s: number out of range on line %d, skipping", 177 | anacrontab, line_num); 178 | return; 179 | } 180 | jr = obstack_alloc(&tab_o, sizeof(job_rec)); 181 | if (jr == NULL) { 182 | die_e("Cannot allocate memory."); 183 | } 184 | jr->period = period; 185 | jr->named_period = 0; 186 | delay += random_number; 187 | jr->delay = delay; 188 | jr->tab_line = line_num; 189 | jr->ident = obstack_alloc(&tab_o, ident_len + 1); 190 | if (jr->ident == NULL) { 191 | die_e("Cannot allocate memory."); 192 | } 193 | strcpy(jr->ident, ident); 194 | jr->arg_num = job_arg_num(ident); 195 | jr->command = obstack_alloc(&tab_o, command_len + 1); 196 | if (jr->command == NULL) { 197 | die_e("Cannot allocate memory."); 198 | } 199 | strcpy(jr->command, command); 200 | jr->job_pid = jr->mailer_pid = 0; 201 | if (last_job_rec != NULL) last_job_rec->next = jr; 202 | else first_job_rec = jr; 203 | last_job_rec = jr; 204 | jr->prev_env_rec = last_env_rec; 205 | jr->next = NULL; 206 | Debug(("Read job - period=%d, delay=%d, ident=%s, command=%s", 207 | jr->period, jr->delay, jr->ident, jr->command)); 208 | } 209 | 210 | static void 211 | register_period_job(const char *periods, const char *delays, 212 | const char *ident, char *command) 213 | /* Store a job definition with a named period */ 214 | { 215 | int delay; 216 | job_rec *jr; 217 | int ident_len, command_len; 218 | 219 | ident_len = (int)strlen(ident); 220 | command_len = (int)strlen(command); 221 | jobs_read++; 222 | delay = conv2int(delays); 223 | if (delay < 0) 224 | { 225 | complain("%s: number out of range on line %d, skipping", 226 | anacrontab, line_num); 227 | return; 228 | } 229 | 230 | jr = obstack_alloc(&tab_o, sizeof(job_rec)); 231 | if (jr == NULL) { 232 | die_e("Cannot allocate memory."); 233 | } 234 | if (!strncmp ("@monthly", periods, 8)) { 235 | jr->named_period = 1; 236 | } else if (!strncmp("@yearly", periods, 7) || !strncmp("@annually", periods, 9) || !strncmp(/* backwards compat misspelling */"@annualy", periods, 8)) { 237 | jr->named_period = 2; 238 | } else if (!strncmp ("@daily", periods, 6)) { 239 | jr->named_period = 3; 240 | } else if (!strncmp ("@weekly", periods, 7)) { 241 | jr->named_period = 4; 242 | } else { 243 | complain("%s: Unknown named period on line %d, skipping", 244 | anacrontab, line_num); 245 | } 246 | jr->period = 0; 247 | delay += random_number; 248 | jr->delay = delay; 249 | jr->tab_line = line_num; 250 | jr->ident = obstack_alloc(&tab_o, ident_len + 1); 251 | if (jr->ident == NULL) { 252 | die_e("Cannot allocate memory."); 253 | } 254 | strcpy(jr->ident, ident); 255 | jr->arg_num = job_arg_num(ident); 256 | jr->command = obstack_alloc(&tab_o, command_len + 1); 257 | if (jr->command == NULL) { 258 | die_e("Cannot allocate memory."); 259 | } 260 | strcpy(jr->command, command); 261 | jr->job_pid = jr->mailer_pid = 0; 262 | if (last_job_rec != NULL) last_job_rec->next = jr; 263 | else first_job_rec = jr; 264 | last_job_rec = jr; 265 | jr->prev_env_rec = last_env_rec; 266 | jr->next = NULL; 267 | Debug(("Read job - period %d, delay=%d, ident%s, command=%s", 268 | jr->named_period, jr->delay, jr->ident, jr->command)); 269 | } 270 | 271 | static long int 272 | unbiased_rand(long int max) 273 | { 274 | long int rn; 275 | long int divisor; 276 | 277 | divisor = RAND_MAX / (max + 1); 278 | 279 | do { 280 | rn = random() / divisor; 281 | } while (rn > max); 282 | 283 | return rn; 284 | } 285 | 286 | static void 287 | parse_tab_line(char *line) 288 | { 289 | int r; 290 | char *env_var; 291 | char *value; 292 | char *periods; 293 | char *delays; 294 | char *ident; 295 | char *command; 296 | char *from; 297 | char *to; 298 | char *pref_hour; 299 | 300 | /* an empty line? */ 301 | r = match_rx("^[ \t]*($|#)", line, 0); 302 | if (r == -1) goto reg_err; 303 | if (r) 304 | { 305 | Debug(("line %d empty", line_num)); 306 | return; 307 | } 308 | 309 | /* an environment assignment? */ 310 | r = match_rx("^[ \t]*([^ \t=]+)[ \t]*=(.*)$", line, 2, 311 | &env_var, &value); 312 | if (r == -1) goto reg_err; 313 | if (r) 314 | { 315 | if (strncmp(env_var, "START_HOURS_RANGE", 17) == 0) 316 | { 317 | r = match_rx("^([[:digit:]]+)-([[:digit:]]+)$", value, 2, &from, &to); 318 | if (r == -1) goto reg_err; 319 | if (r == 0) goto reg_invalid; 320 | range_start = atoi(from); 321 | range_stop = atoi(to); 322 | if (range_stop < range_start) { 323 | range_start = 0; range_stop = 0; 324 | goto reg_invalid; 325 | } 326 | Debug(("Jobs will start in the %02d:00-%02d:00 range.", range_start, range_stop)); 327 | } 328 | else if (strncmp(env_var, "RANDOM_DELAY", 12) == 0) { 329 | r = match_rx("^([[:digit:]]+)$", value, 0); 330 | if (r == -1) goto reg_err; 331 | if (r == 0) goto reg_invalid; 332 | 333 | random_number = (int)unbiased_rand(atoi(value)); 334 | Debug(("Randomized delay set: %d", random_number)); 335 | } 336 | else if (strncmp(env_var, "PREFERRED_HOUR", 14) == 0) { 337 | r = match_rx("^([[:digit:]]+)$", value, 1, &pref_hour); 338 | if (r == -1) goto reg_err; 339 | 340 | if (r) { 341 | preferred_hour = atoi(pref_hour); 342 | if ((preferred_hour < 0) || (preferred_hour > 24)) { 343 | preferred_hour = -1; 344 | goto reg_invalid; 345 | } 346 | } 347 | } 348 | register_env(env_var, value); 349 | return; 350 | } 351 | 352 | /* a job? */ 353 | r = match_rx("^[ \t]*([[:digit:]]+)[ \t]+([[:digit:]]+)[ \t]+" 354 | "([^ \t/]+)[ \t]+([^ \t].*)$", 355 | line, 4, &periods, &delays, &ident, &command); 356 | if (r == -1) goto reg_err; 357 | if (r) 358 | { 359 | register_job(periods, delays, ident, command); 360 | return; 361 | } 362 | 363 | /* A period job? */ 364 | r = match_rx("^[ \t]*(@[^ \t]+)[ \t]+([[:digit:]]+)[ \t]+" 365 | "([^ \t/]+)[ \t]+([^ \t].*)$", 366 | line, 4, &periods, &delays, &ident, &command); 367 | if (r == -1) goto reg_err; 368 | if (r) 369 | { 370 | register_period_job(periods, delays, ident, command); 371 | return; 372 | } 373 | 374 | reg_invalid: 375 | complain("Invalid syntax in %s on line %d - skipping this line", 376 | anacrontab, line_num); 377 | return; 378 | 379 | reg_err: 380 | die("Regex error reading %s", anacrontab); 381 | } 382 | 383 | void 384 | read_tab(int cwd) 385 | /* Read the anacrontab file into memory */ 386 | { 387 | char *tab_line; 388 | 389 | first_job_rec = last_job_rec = NULL; 390 | first_env_rec = last_env_rec = NULL; 391 | jobs_read = 0; 392 | line_num = 0; 393 | /* Open the anacrontab file */ 394 | if (fchdir(cwd)) die_e("Can't chdir to original cwd"); 395 | tab = fopen(anacrontab, "r"); 396 | if (chdir(spooldir)) die_e("Can't chdir to %s", spooldir); 397 | 398 | if (tab == NULL) die_e("Error opening %s", anacrontab); 399 | /* Initialize the obstacks */ 400 | obstack_init(&input_o); 401 | obstack_init(&tab_o); 402 | while ((tab_line = read_tab_line()) != NULL) 403 | { 404 | line_num++; 405 | parse_tab_line(tab_line); 406 | obstack_free(&input_o, tab_line); 407 | } 408 | if (fclose(tab)) die_e("Error closing %s", anacrontab); 409 | } 410 | 411 | static int 412 | execution_order(const job_rec **job1, const job_rec **job2) 413 | /* Comparison function for sorting the jobs. 414 | */ 415 | { 416 | int d; 417 | 418 | d = (*job1)->arg_num - (*job2)->arg_num; 419 | if (d != 0 && now) return d; 420 | d = (*job1)->delay - (*job2)->delay; 421 | if (d != 0) return d; 422 | d = (*job1)->tab_line - (*job2)->tab_line; 423 | return d; 424 | } 425 | 426 | void 427 | arrange_jobs(void) 428 | /* Make an array of pointers to jobs that are going to be executed, 429 | * and arrange them in the order of execution. 430 | * Also lock these jobs. 431 | */ 432 | { 433 | job_rec *j; 434 | 435 | j = first_job_rec; 436 | njobs = 0; 437 | while (j != NULL) 438 | { 439 | if (j->arg_num != -1 && (update_only || testing_only || consider_job(j))) 440 | { 441 | njobs++; 442 | obstack_grow(&tab_o, &j, sizeof(j)); 443 | } 444 | j = j->next; 445 | } 446 | job_array = obstack_finish(&tab_o); 447 | 448 | /* sort the jobs */ 449 | qsort(job_array, (size_t)njobs, sizeof(*job_array), 450 | (int (*)(const void *, const void *))execution_order); 451 | } 452 | -------------------------------------------------------------------------------- /anacron/runjob.c: -------------------------------------------------------------------------------- 1 | /* 2 | Anacron - run commands periodically 3 | Copyright (C) 1998 Itai Tzur 4 | Copyright (C) 1999 Sean 'Shaleh' Perry 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License along 17 | with this program; if not, write to the Free Software Foundation, Inc., 18 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | 20 | The GNU General Public License can also be found in the file 21 | `COPYING' that comes with the Anacron source distribution. 22 | */ 23 | 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include "global.h" 38 | #include "cronie_common.h" 39 | 40 | #include 41 | 42 | static int 43 | temp_file(job_rec *jr) 44 | /* Open a temporary file and return its file descriptor */ 45 | { 46 | char *dir; 47 | char template[PATH_MAX+1]; 48 | int fdin = -1; 49 | int fdout; 50 | int len; 51 | 52 | dir = getenv("TMPDIR"); 53 | if (dir == NULL || *dir == '\0') 54 | dir = P_tmpdir; 55 | 56 | len = snprintf(template, sizeof(template), "%s/$anacronXXXXXX", dir); 57 | if (len < 0) 58 | die_e("snprintf failed"); 59 | else if ((size_t) len >= sizeof(template)) 60 | die_e("TMPDIR too long"); 61 | 62 | fdout = mkstemp(template); 63 | if (fdout == -1) die_e("Can't open temporary file for writing"); 64 | 65 | fdin = open(template, O_RDONLY, S_IRUSR | S_IWUSR); 66 | if (fdin == -1) die_e("Can't open temporary file for reading"); 67 | 68 | if (unlink(template)) die_e("Can't unlink temporary file"); 69 | 70 | fcntl(fdout, F_SETFD, FD_CLOEXEC); /* set close-on-exec flag */ 71 | fcntl(fdin, F_SETFD, FD_CLOEXEC); /* set close-on-exec flag */ 72 | 73 | jr->input_fd = fdin; 74 | jr->output_fd = fdout; 75 | 76 | return fdout; 77 | } 78 | 79 | static off_t 80 | file_size(int fd) 81 | /* Return the size of temporary file fd */ 82 | { 83 | struct stat st; 84 | 85 | if (fstat(fd, &st)) die_e("Can't fstat temporary file"); 86 | return st.st_size; 87 | } 88 | 89 | static char * 90 | username(void) 91 | { 92 | struct passwd *ps; 93 | static char *user; 94 | 95 | if (user) 96 | return user; 97 | 98 | ps = getpwuid(geteuid()); 99 | if (ps == NULL || ps->pw_name == NULL) die_e("getpwuid() error"); 100 | 101 | user = strdup(ps->pw_name); 102 | if (user == NULL) die_e("memory allocation error"); 103 | 104 | return user; 105 | } 106 | 107 | static void 108 | xputenv(const char *s) 109 | { 110 | char *name = NULL, *val = NULL; 111 | char *eq_ptr; 112 | size_t eq_index; 113 | 114 | if (s == NULL) { 115 | die_e("Invalid environment string"); 116 | } 117 | 118 | eq_ptr = strchr(s, '='); 119 | if (eq_ptr == NULL) { 120 | die_e("Invalid environment string"); 121 | } 122 | 123 | eq_index = (size_t) (eq_ptr - s); 124 | 125 | name = malloc((eq_index + 1) * sizeof(char)); 126 | if (name == NULL) { 127 | die_e("Not enough memory to set the environment"); 128 | } 129 | 130 | val = malloc((strlen(s) - eq_index) * sizeof(char)); 131 | if (val == NULL) { 132 | die_e("Not enough memory to set the environment"); 133 | } 134 | 135 | strncpy(name, s, eq_index); 136 | name[eq_index] = '\0'; 137 | strcpy(val, s + eq_index + 1); 138 | 139 | if (setenv(name, val, 1)) { 140 | die_e("Can't set the environment"); 141 | } 142 | 143 | free(name); 144 | free(val); 145 | return; 146 | 147 | } 148 | 149 | static void 150 | setup_env(const job_rec *jr) 151 | /* Setup the environment for the job according to /etc/anacrontab */ 152 | { 153 | env_rec *er; 154 | 155 | er = first_env_rec; 156 | if (er == NULL || jr->prev_env_rec == NULL) return; 157 | xputenv(er->assign); 158 | while (er != jr->prev_env_rec) 159 | { 160 | er = er->next; 161 | xputenv(er->assign); 162 | } 163 | } 164 | 165 | static void 166 | run_job(const job_rec *jr) 167 | /* This is called to start the job, after the fork */ 168 | { 169 | /* If mail functionality is not disabled, pipe outputs to temp file */ 170 | if (!jr->no_mail_output) { 171 | /* setup stdout and stderr */ 172 | xclose(1); 173 | xclose(2); 174 | if (dup2(jr->output_fd, 1) != 1 || dup2(jr->output_fd, 2) != 2) 175 | die_e("dup2() error"); /* dup2 also clears close-on-exec flag */ 176 | in_background = 0; /* now, errors will be mailed to the user */ 177 | } 178 | 179 | if (chdir("/")) die_e("Can't chdir to '/'"); 180 | 181 | if (sigprocmask(SIG_SETMASK, &old_sigmask, NULL)) 182 | die_e("sigprocmask error"); 183 | xcloselog(); 184 | execl("/bin/sh", "/bin/sh", "-c", jr->command, (char *)NULL); 185 | die_e("execl() error"); 186 | } 187 | 188 | static void 189 | xwrite(int fd, const char *string) 190 | /* Write (using write()) the string "string" to temporary file "fd". 191 | * Don't return on failure */ 192 | { 193 | if (write(fd, string, strlen(string)) == -1) 194 | die_e("Can't write to temporary file"); 195 | } 196 | 197 | static int 198 | xwait(pid_t pid , int *status) 199 | /* Check if child process "pid" has finished. If it has, return 1 and its 200 | * exit status in "*status". If not, return 0. 201 | */ 202 | { 203 | pid_t r; 204 | 205 | r = waitpid(pid, status, WNOHANG); 206 | if (r == -1) die_e("waitpid() error"); 207 | if (r == 0) return 0; 208 | return 1; 209 | } 210 | 211 | static void 212 | launch_mailer(job_rec *jr) 213 | { 214 | pid_t pid; 215 | struct stat buf; 216 | 217 | if (jr->mailto == NULL) 218 | { 219 | explain("Empty MAILTO set, not mailing output"); 220 | return; 221 | } 222 | 223 | /* Check that we have a way of sending mail. */ 224 | if(stat(SENDMAIL, &buf)) 225 | { 226 | complain("Can't find sendmail at %s, not mailing output", SENDMAIL); 227 | return; 228 | } 229 | 230 | pid = xfork(); 231 | if (pid == 0) 232 | { 233 | /* child */ 234 | in_background = 1; 235 | /* set stdin to the job's output */ 236 | xclose(STDIN_FILENO); 237 | if (dup2(jr->input_fd, STDIN_FILENO) != 0) die_e("Can't dup2()"); 238 | if (lseek(STDIN_FILENO, 0, SEEK_SET) != 0) die_e("Can't lseek()"); 239 | if (sigprocmask(SIG_SETMASK, &old_sigmask, NULL)) 240 | die_e("sigprocmask error"); 241 | xcloselog(); 242 | 243 | /* Ensure stdout/stderr are sane before exec-ing sendmail */ 244 | /* coverity[leaked_handle] – STDOUT closed automatically */ 245 | xclose(STDOUT_FILENO); xopen(STDOUT_FILENO, "/dev/null", O_WRONLY); 246 | /* coverity[leaked_handle] – STDERR closed automatically */ 247 | xclose(STDERR_FILENO); xopen(STDERR_FILENO, "/dev/null", O_WRONLY); 248 | xclose(jr->output_fd); 249 | 250 | /* Ensure stdin is not appendable ... ? */ 251 | /* fdflags = fcntl(0, F_GETFL); fdflags &= ~O_APPEND; */ 252 | /* fcntl(0, F_SETFL, fdflags ); */ 253 | 254 | /* Here, I basically mirrored the way /usr/sbin/sendmail is called 255 | * by cron on a Debian system, except for the "-oem" and "-or0s" 256 | * options, which don't seem to be appropriate here. 257 | * Hopefully, this will keep all the MTAs happy. */ 258 | execl(SENDMAIL, SENDMAIL, "-FAnacron", "-odi", 259 | jr->mailto, (char *)NULL); 260 | die_e("Can't exec " SENDMAIL); 261 | } 262 | /* parent */ 263 | /* record mailer pid */ 264 | jr->mailer_pid = pid; 265 | running_mailers++; 266 | } 267 | 268 | static void 269 | tend_mailer(job_rec *jr, int status) 270 | { 271 | if (WIFEXITED(status) && WEXITSTATUS(status) != 0) 272 | complain("Tried to mail output of job `%s', " 273 | "but mailer process (" SENDMAIL ") exited with status %d", 274 | jr->ident, WEXITSTATUS(status)); 275 | else if (!WIFEXITED(status) && WIFSIGNALED(status)) 276 | complain("Tried to mail output of job `%s', " 277 | "but mailer process (" SENDMAIL ") got signal %d", 278 | jr->ident, WTERMSIG(status)); 279 | else if (!WIFEXITED(status) && !WIFSIGNALED(status)) 280 | complain("Tried to mail output of job `%s', " 281 | "but mailer process (" SENDMAIL ") terminated abnormally" 282 | , jr->ident); 283 | 284 | jr->mailer_pid = 0; 285 | running_mailers--; 286 | } 287 | 288 | void 289 | launch_job(job_rec *jr) 290 | { 291 | pid_t pid; 292 | int fd; 293 | char hostname[512]; 294 | char *mailto; 295 | char *mailfrom; 296 | char mailto_expanded[MAX_EMAILSTR]; 297 | char mailfrom_expanded[MAX_EMAILSTR]; 298 | char *no_mail_output; 299 | 300 | /* get hostname */ 301 | if (gethostname(hostname, 512)) { 302 | strcpy (hostname,"unknown machine"); 303 | } 304 | 305 | setup_env(jr); 306 | 307 | no_mail_output = getenv("NO_MAIL_OUTPUT"); 308 | jr->no_mail_output = no_mail_output != NULL && *no_mail_output; 309 | 310 | /* Set up email functionality if it isn't disabled */ 311 | if (!jr->no_mail_output) { 312 | /* Get the destination email address if set, or current user otherwise */ 313 | mailto = getenv("MAILTO"); 314 | if (mailto == NULL) { 315 | mailto = username(); 316 | } 317 | else { 318 | if (expand_envvar(mailto, mailto_expanded, sizeof(mailto_expanded))) { 319 | mailto = mailto_expanded; 320 | } 321 | else { 322 | complain("The environment variable 'MAILTO' could not be expanded. The non-expanded value will be used."); 323 | } 324 | } 325 | if (mailto != NULL && (mailto = strdup(mailto)) == NULL) { 326 | complain("Strdup failed for MAILTO, job not run"); 327 | return; 328 | } 329 | 330 | /* Get the source email address if set, or current user otherwise */ 331 | mailfrom = getenv("MAILFROM"); 332 | if (mailfrom == NULL) { 333 | mailfrom = username(); 334 | } 335 | else { 336 | if (expand_envvar(mailfrom, mailfrom_expanded, sizeof(mailfrom_expanded))) { 337 | mailfrom = mailfrom_expanded; 338 | } 339 | else { 340 | complain("The environment variable 'MAILFROM' could not be expanded. The non-expanded value will be used."); 341 | } 342 | } 343 | 344 | /* create temporary file for stdout and stderr of the job */ 345 | temp_file(jr); fd = jr->output_fd; 346 | /* write mail header */ 347 | xwrite(fd, "From: "); 348 | xwrite(fd, "Anacron <"); 349 | xwrite(fd, mailfrom); 350 | xwrite(fd, ">\n"); 351 | xwrite(fd, "To: "); 352 | xwrite(fd, mailto); 353 | xwrite(fd, "\n"); 354 | xwrite(fd, "MIME-Version: 1.0\n"); 355 | xwrite(fd, "Content-Type: text/plain; charset=\""); 356 | xwrite(fd, nl_langinfo(CODESET)); 357 | xwrite(fd, "\"\n"); 358 | xwrite(fd, "Content-Transfer-Encoding: 8bit\n"); 359 | xwrite(fd, "Subject: Anacron job '"); 360 | xwrite(fd, jr->ident); 361 | xwrite(fd, "' on "); 362 | xwrite(fd, hostname); 363 | xwrite(fd, "\n\n"); 364 | 365 | if (*mailto == '\0') 366 | jr->mailto = NULL; 367 | else 368 | jr->mailto = mailto; 369 | 370 | jr->mail_header_size = file_size(fd); 371 | } 372 | 373 | pid = xfork(); 374 | if (pid == 0) 375 | { 376 | /* child */ 377 | in_background = 1; 378 | run_job(jr); 379 | /* execution never gets here */ 380 | } 381 | /* parent */ 382 | explain("Job `%s' started", jr->ident); 383 | jr->job_pid = pid; 384 | running_jobs++; 385 | } 386 | 387 | static void 388 | tend_job(job_rec *jr, int status) 389 | /* Take care of a finished job */ 390 | { 391 | int mail_output; 392 | const char *m; 393 | 394 | update_timestamp(jr); 395 | unlock(jr); 396 | 397 | mail_output = !jr->no_mail_output && file_size(jr->output_fd) > jr->mail_header_size; 398 | 399 | m = mail_output ? " (produced output)" : ""; 400 | if (WIFEXITED(status) && WEXITSTATUS(status) == 0) 401 | explain("Job `%s' terminated%s", jr->ident, m); 402 | else if (WIFEXITED(status)) 403 | explain("Job `%s' terminated (exit status: %d)%s", 404 | jr->ident, WEXITSTATUS(status), m); 405 | else if (WIFSIGNALED(status)) 406 | complain("Job `%s' terminated due to signal %d%s", 407 | jr->ident, WTERMSIG(status), m); 408 | else /* is this possible? */ 409 | complain("Job `%s' terminated abnormally%s", jr->ident, m); 410 | 411 | jr->job_pid = 0; 412 | running_jobs--; 413 | if (mail_output) launch_mailer(jr); 414 | /* output_fd and input_fd are only set if mail functionality is enabled */ 415 | if (!jr->no_mail_output) { 416 | xclose(jr->output_fd); 417 | xclose(jr->input_fd); 418 | } 419 | } 420 | 421 | void 422 | tend_children(void) 423 | /* This is called whenever we get a SIGCHLD. 424 | * Takes care of zombie children. 425 | */ 426 | { 427 | int j; 428 | int status; 429 | 430 | j = 0; 431 | while (j < njobs) 432 | { 433 | if (job_array[j]->mailer_pid != 0 && 434 | xwait(job_array[j]->mailer_pid, &status)) 435 | tend_mailer(job_array[j], status); 436 | if (job_array[j]->job_pid != 0 && 437 | xwait(job_array[j]->job_pid, &status)) 438 | tend_job(job_array[j], status); 439 | j++; 440 | } 441 | } 442 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Generate autotool stuff, when code is checked out from SCM. 4 | 5 | SCRIPT_INVOCATION_SHORT_NAME=${0##*/} 6 | set -e # exit on errors 7 | # trap ERR is bashism, do not change shebang! 8 | trap 'echo "${SCRIPT_INVOCATION_SHORT_NAME}: exit on error"; exit 1' ERR 9 | set -o pipefail # make pipe writer failure to cause exit on error 10 | 11 | msg() { 12 | echo "${SCRIPT_INVOCATION_SHORT_NAME}: ${@}" 13 | } 14 | 15 | test -f src/cron.c || { 16 | msg "You must run this script in the top-level cronie directory" 17 | exit 1 18 | } 19 | 20 | rm -rf autom4te.cache 21 | 22 | aclocal $AL_OPTS 23 | autoconf $AC_OPTS 24 | autoheader $AH_OPTS 25 | automake --add-missing $AM_OPTS 26 | 27 | msg "Now type './configure' and 'make' to compile." 28 | 29 | exit 0 30 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([cronie],[1.7.2]) 2 | AC_CONFIG_HEADERS([config.h]) 3 | AC_PREREQ([2.64]) 4 | 5 | AM_INIT_AUTOMAKE([subdir-objects]) 6 | 7 | m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])], 8 | [AC_SUBST([AM_DEFAULT_VERBOSITY], [1])]) 9 | 10 | AC_CANONICAL_HOST 11 | 12 | dnl Checks for programs. 13 | 14 | AC_PROG_CC 15 | AC_PROG_INSTALL 16 | AC_PROG_LN_S 17 | 18 | dnl Check for _GNU_SOURCE 19 | AC_USE_SYSTEM_EXTENSIONS 20 | 21 | AC_CHECK_HEADERS( \ 22 | dirent.h \ 23 | fcntl.h \ 24 | getopt.h \ 25 | glob.h \ 26 | limits.h \ 27 | paths.h \ 28 | pty.h \ 29 | selinux/selinux.h \ 30 | stddef.h \ 31 | stdint.h \ 32 | sys/audit.h \ 33 | sys/inotify.h \ 34 | sys/stat.h \ 35 | sys/stream.h \ 36 | sys/stropts.h \ 37 | sys/time.h \ 38 | sys/timers.h \ 39 | sys/types.h \ 40 | sys/cdefs.h \ 41 | time.h \ 42 | unistd.h \ 43 | util.h \ 44 | utime.h \ 45 | ) 46 | 47 | AC_CHECK_FUNCS( \ 48 | fcntl \ 49 | lockf \ 50 | flock \ 51 | fchown \ 52 | fchgrp \ 53 | ) 54 | 55 | dnl Checks for typedefs, structures, and compiler characteristics. 56 | AC_C_CONST 57 | AC_TYPE_UID_T 58 | AC_TYPE_MODE_T 59 | AC_TYPE_OFF_T 60 | AC_TYPE_PID_T 61 | AC_TYPE_SIZE_T 62 | AC_STRUCT_TM 63 | AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[#include ]) 64 | 65 | dnl Checking for programs 66 | 67 | AC_ARG_WITH([editor], 68 | [AS_HELP_STRING([--with-editor=EDITOR],[path to default editor])], 69 | [editor_defined="$with_editor"], 70 | [editor_defined="no"]) 71 | AS_IF([test "x$editor_defined" = "xno"], [ 72 | AC_PATH_PROG([editor_defined], [vi], [/usr/bin/vi]) 73 | ]) 74 | AC_DEFINE_UNQUOTED([EDITOR], ["$editor_defined"], [default editor]) 75 | 76 | AC_MSG_CHECKING(username to run under) 77 | AC_ARG_WITH(daemon_username, 78 | [AS_HELP_STRING([--with-daemon_username=DAEMON_USERNAME], [Username to run under (default daemon) ])], 79 | [ case "$withval" in 80 | no) 81 | AC_MSG_ERROR(Need DAEMON_USERNAME.) 82 | ;; 83 | yes) 84 | DAEMON_USERNAME=daemon 85 | AC_MSG_RESULT(daemon) 86 | ;; 87 | *) 88 | DAEMON_USERNAME="$withval"; 89 | AC_MSG_RESULT($withval) 90 | ;; 91 | esac ], 92 | DAEMON_USERNAME=daemon 93 | AC_MSG_RESULT(daemon) 94 | ) 95 | AC_SUBST(DAEMON_USERNAME) 96 | 97 | AC_MSG_CHECKING(groupname to run under) 98 | AC_ARG_WITH(daemon_groupname, 99 | [AS_HELP_STRING([--with-daemon_groupname=DAEMON_GROUPNAME], [Groupname to run under (default daemon) ])], 100 | [ case "$withval" in 101 | no) 102 | AC_MSG_ERROR(Need DAEMON_GROUPNAME.) 103 | ;; 104 | yes) 105 | DAEMON_GROUPNAME=daemon 106 | AC_MSG_RESULT(daemon) 107 | ;; 108 | *) 109 | DAEMON_GROUPNAME="$withval"; 110 | AC_MSG_RESULT($withval) 111 | ;; 112 | esac ], 113 | DAEMON_GROUPNAME=daemon 114 | AC_MSG_RESULT(daemon) 115 | ) 116 | AC_SUBST(DAEMON_GROUPNAME) 117 | 118 | # Check whether inotify is accepted 119 | AC_ARG_WITH(inotify, 120 | [AS_HELP_STRING([--with-inotify], [ Enable inotify support])], 121 | [ if test "x$withval" != "xno" ; then 122 | AC_DEFINE(WITH_INOTIFY,1,[Define if you want inotify support.]) 123 | AC_CHECK_HEADER([sys/inotify.h], , AC_MSG_ERROR(Inotify support requires sys/inotify.h header)) 124 | AC_CHECK_FUNCS(inotify_init inotify_add_watch) 125 | fi 126 | ] 127 | ) 128 | 129 | AC_ARG_ENABLE(pie,CRONIE_HELP_STRING(--enable-pie,Build cronie as a Position Independent Executable)) 130 | if test "x$enable_pie" = xyes; then 131 | CFLAGS="$CFLAGS -fPIE -DPIE" 132 | LDFLAGS="$LDFLAGS -pie" 133 | fi 134 | 135 | AC_ARG_ENABLE(relro,CRONIE_HELP_STRING(--enable-relro,Build cronie with relro flag)) 136 | if test "x$enable_relro" = xyes; then 137 | LDFLAGS="$LDFLAGS -Wl,-z,relro -Wl,-z,now" 138 | fi 139 | 140 | AC_ARG_ENABLE(bsd, BSD_STRING(--enable-bsd,Build cronie with BSD specific parts)) 141 | 142 | # Check whether user wants SELinux support 143 | SELINUX_MSG="no" 144 | LIBSELINUX="" 145 | AC_ARG_WITH(selinux, 146 | [AS_HELP_STRING([--with-selinux], [Enable SELinux support])], 147 | [ if test "x$withval" != "xno" ; then 148 | saved_LIBS="$LIBS" 149 | AC_DEFINE(WITH_SELINUX,1,[Define if you want SELinux support.]) 150 | SELINUX_MSG="yes" 151 | AC_CHECK_HEADER([selinux/selinux.h], ,AC_MSG_ERROR(SELinux support requires selinux.h header)) 152 | AC_CHECK_LIB(selinux, setexeccon, [ LIBSELINUX="-lselinux" ], 153 | AC_MSG_ERROR(SELinux support requires libselinux library)) 154 | AC_CHECK_FUNCS(getseuserbyname get_default_context_with_level) 155 | LIBS="$saved_LIBS" 156 | AC_SUBST(LIBSELINUX) 157 | fi ] 158 | ) 159 | 160 | AC_ARG_WITH(pam, [AS_HELP_STRING([--with-pam], [Build with PAM support])]) 161 | AC_ARG_ENABLE(pam, [AS_HELP_STRING([--enable-pam], [Alias for --with-pam])]) 162 | 163 | # Check that with_pam and enable_pam are consistent. 164 | # If neither one is set, the default is "no." 165 | if test -z "$with_pam"; then 166 | with_pam=${enable_pam:-no} 167 | elif test -n "$enable_pam" && test "$with_pam" != "$enable_pam"; then 168 | AC_MSG_ERROR( 169 | [Contradicting --with/without-pam and --enable/disable-pam options.]) 170 | fi 171 | 172 | AM_CONDITIONAL([PAM], [test "$with_pam" != no]) 173 | 174 | if test "$with_pam" != no; then 175 | AC_DEFINE(WITH_PAM, 1, [Define if you want to enable PAM support]) 176 | pam_appl_h_found=no 177 | AC_CHECK_HEADERS([pam/pam_appl.h security/pam_appl.h], 178 | [pam_appl_h_found=yes]) 179 | test "$pam_appl_h_found" = yes || 180 | AC_MSG_ERROR([PAM headers not found]) 181 | 182 | saved_LIBS="$LIBS" 183 | AC_CHECK_LIB([dl], [dlopen], [libdl_found=yes], [libdl_found=no]) 184 | AC_CHECK_LIB(pam, pam_set_item, , AC_MSG_ERROR([*** libpam missing])) 185 | AC_CHECK_FUNCS([pam_getenvlist pam_putenv]) 186 | LIBS="$saved_LIBS" 187 | 188 | case $libdl_found:" $LIBS " in #( 189 | *" -ldl "*) LIBPAM= ;; #( 190 | yes:*) LIBPAM=-ldl ;; # libdl found, but is not in $LIBS 191 | esac 192 | AC_SUBST([LIBPAM], ["-lpam $LIBPAM"]) 193 | fi 194 | 195 | AC_DEFINE(DEBUGGING,1,[Code will be built with debug info.]) 196 | 197 | AC_DEFINE(MAILARG,"/usr/sbin/sendmail",[There will be path to sendmail.]) 198 | 199 | AC_DEFINE(MAILFMT,"%s -FCronDaemon -i -odi -oem -oi -t -f %s", 200 | [-i = don't terminate on "." by itself 201 | -Fx = Set full-name of sender 202 | -odi = Option Deliverymode Interactive 203 | -oem = Option Errors Mailedtosender 204 | -oi = Ignore "." alone on a line 205 | -t = Get recipient from headers 206 | -f %s = Envelope sender address 207 | -d = undocumented but common flag.]) 208 | 209 | AC_DEFINE(SYSLOG,1,[Using syslog for log messages.]) 210 | 211 | AC_DEFINE(CAPITALIZE_FOR_PS, 1, [if you have a tm_gmtoff member in struct tm]) 212 | 213 | # Check whether user wants Linux audit support 214 | AC_ARG_WITH(audit, 215 | [AS_HELP_STRING([--with-audit], [Enable audit trails])], 216 | [ if test "x$withval" != "xno" ; then 217 | saved_LIBS="$LIBS" 218 | AC_DEFINE(WITH_AUDIT,1,[Define if you want Audit trails.]) 219 | AC_CHECK_HEADER([libaudit.h], ,AC_MSG_ERROR(Audit trails requires libaudit.h header)) 220 | AC_CHECK_LIB(audit, audit_open, [ LIBAUDIT="-laudit" ], 221 | AC_MSG_ERROR(Audit support needs audit libraries.)) 222 | LIBS="$saved_LIBS" 223 | AC_SUBST(LIBAUDIT) 224 | fi ] 225 | ) 226 | 227 | AC_ARG_ENABLE(syscrontab, 228 | [AS_HELP_STRING([--enable-syscrontab], [Build cronie with system crontab enabled.])], 229 | [ if test "x$enableval" != xno; then 230 | AC_DEFINE(ENABLE_SYSCRONTAB,1,[Define if you want system crontab.]) 231 | fi ], [AC_DEFINE(ENABLE_SYSCRONTAB,1,[Define if you want system crontab.])] 232 | ) 233 | 234 | dnl CRONIE_VAR_DEFAULT (VAR, DESCRIPTION, DEFAULT) 235 | dnl -------------------------------------------- 236 | AC_DEFUN([CRONIE_CONF_VAR], 237 | [AC_ARG_VAR([$1], [$2 @<:@$3@:>@]) 238 | if test "$$1" = ""; then 239 | $1='$3' 240 | fi 241 | ]) 242 | 243 | AC_DEFUN([ANACRON_CONF_VAR], 244 | [AC_ARG_VAR([$1], [$2 @<:@$3@:>@]) 245 | if test "$$1" = ""; then 246 | $1='$3' 247 | fi 248 | ]) 249 | 250 | CRONIE_CONF_VAR([SYSCRONTAB], [the current working directory of the running daemon], [${sysconfdir}/crontab]) 251 | CRONIE_CONF_VAR([SYS_CROND_DIR], [the current working directory of the running daemon], [${sysconfdir}/cron.d]) 252 | CRONIE_CONF_VAR([SPOOL_DIR], [the directory where all the user cron tabs reside], [${localstatedir}/spool/cron]) 253 | 254 | AC_ARG_ENABLE([anacron], [AS_HELP_STRING([--disable-anacron], [Do not build anacron.])], [], [enable_anacron=yes]) 255 | AM_CONDITIONAL([ANACRON], [test "$enable_anacron" = yes]) 256 | if test "$enable_anacron" != no; then 257 | ANACRON_CONF_VAR([ANACRON_SPOOL_DIR],[The path for anacron locks.],[${localstatedir}/spool/anacron]) 258 | ANACRON_CONF_VAR([ANACRONTAB],[The anacron table for regular jobs.],[${sysconfdir}/anacrontab]) 259 | 260 | dnl obstack.h is part of GLIBC and may not be present on other systems, 261 | dnl eg. musl and AIX. There, we static link in our own copy. 262 | AC_CHECK_HEADER(obstack.h, [have_obstack=yes], [have_obstack=no], []) 263 | fi 264 | AM_CONDITIONAL([NEED_OBSTACK], [test "$have_obstack" = no]) 265 | AS_IF([test "$enable_anacron" != no && test "$have_obstack" = no], [ 266 | CPPFLAGS="$CPPFLAGS -I\$(top_srcdir)/obstack" 267 | ]) 268 | 269 | AM_CONDITIONAL(HAS_RUNSTATE, [test x$runstatedir != x]) 270 | 271 | AC_CONFIG_FILES([Makefile]) 272 | AC_OUTPUT 273 | 274 | -------------------------------------------------------------------------------- /contrib/0anacron: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Check whether 0anacron was run today already 3 | if test -r /var/spool/anacron/cron.daily; then 4 | day=`cat /var/spool/anacron/cron.daily` 5 | fi 6 | if [ `date +%Y%m%d` = "$day" ]; then 7 | exit 0 8 | fi 9 | 10 | # Check whether run on battery should be allowed 11 | if test -r /etc/default/anacron; then 12 | . /etc/default/anacron 13 | fi 14 | 15 | if [ "$ANACRON_RUN_ON_BATTERY_POWER" != "yes" ]; then 16 | 17 | # Do not run jobs when on battery power 18 | online=1 19 | for psupply in /sys/class/power_supply/* ; do 20 | if [ `cat "$psupply/type" 2>/dev/null`x = Mainsx ] && [ -f "$psupply/online" ]; then 21 | if [ `cat "$psupply/online" 2>/dev/null`x = 1x ]; then 22 | online=1 23 | break 24 | else 25 | online=0 26 | fi 27 | fi 28 | done 29 | if [ $online = 0 ]; then 30 | exit 0 31 | fi 32 | 33 | fi 34 | /usr/sbin/anacron -s 35 | -------------------------------------------------------------------------------- /contrib/0hourly: -------------------------------------------------------------------------------- 1 | # Run the hourly jobs 2 | SHELL=/bin/bash 3 | PATH=/sbin:/bin:/usr/sbin:/usr/bin 4 | MAILTO=root 5 | 01 * * * * root run-parts /etc/cron.hourly 6 | -------------------------------------------------------------------------------- /contrib/anacrontab: -------------------------------------------------------------------------------- 1 | # /etc/anacrontab: configuration file for anacron 2 | 3 | # See anacron(8) and anacrontab(5) for details. 4 | 5 | SHELL=/bin/sh 6 | PATH=/sbin:/bin:/usr/sbin:/usr/bin 7 | MAILTO=root 8 | # the maximal random delay added to the base delay of the jobs 9 | RANDOM_DELAY=45 10 | # the jobs will be started during the following hours only 11 | START_HOURS_RANGE=3-22 12 | 13 | #period in days delay in minutes job-identifier command 14 | 1 5 cron.daily nice run-parts /etc/cron.daily 15 | 7 25 cron.weekly nice run-parts /etc/cron.weekly 16 | @monthly 45 cron.monthly nice run-parts /etc/cron.monthly 17 | -------------------------------------------------------------------------------- /contrib/cronie.systemd: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Command Scheduler 3 | After=auditd.service nss-user-lookup.target systemd-user-sessions.service time-sync.target ypbind.service autofs.service 4 | 5 | [Service] 6 | EnvironmentFile=-/etc/sysconfig/crond 7 | ExecStart=/usr/sbin/crond -n $CRONDARGS 8 | ExecReload=/bin/kill -URG $MAINPID 9 | KillMode=process 10 | Restart=on-failure 11 | RestartSec=30s 12 | KeyringMode=private 13 | LockPersonality=yes 14 | MemoryDenyWriteExecute=yes 15 | NoNewPrivileges=no 16 | PrivateDevices=no 17 | PrivateTmp=yes 18 | ProtectClock=yes 19 | ProtectControlGroups=yes 20 | ProtectHome=no 21 | ProtectHostname=yes 22 | ProtectKernelLogs=no 23 | ProtectKernelModules=yes 24 | ProtectKernelTunables=no 25 | ProtectProc=invisible 26 | ProtectSystem=no 27 | RestrictNamespaces=no 28 | RestrictRealtime=yes 29 | RestrictSUIDSGID=no 30 | 31 | [Install] 32 | WantedBy=multi-user.target 33 | 34 | -------------------------------------------------------------------------------- /contrib/dailyjobs: -------------------------------------------------------------------------------- 1 | # Run the daily, weekly, and monthly jobs if cronie-anacron is not installed 2 | SHELL=/bin/bash 3 | PATH=/sbin:/bin:/usr/sbin:/usr/bin 4 | MAILTO=root 5 | 6 | # run-parts 7 | 02 4 * * * root [ ! -f /etc/cron.hourly/0anacron ] && run-parts /etc/cron.daily 8 | 22 4 * * 0 root [ ! -f /etc/cron.hourly/0anacron ] && run-parts /etc/cron.weekly 9 | 42 4 1 * * root [ ! -f /etc/cron.hourly/0anacron ] && run-parts /etc/cron.monthly 10 | -------------------------------------------------------------------------------- /crond.sysconfig: -------------------------------------------------------------------------------- 1 | # Settings for the CRON daemon. 2 | # CRONDARGS= : any extra command-line startup arguments for crond 3 | CRONDARGS= 4 | -------------------------------------------------------------------------------- /cronie.init: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # crond Start/Stop the cron clock daemon. 4 | # 5 | # chkconfig: 2345 90 60 6 | # description: cron is a standard UNIX program that runs user-specified \ 7 | # programs at periodic scheduled times. vixie cron adds a \ 8 | # number of features to the basic UNIX cron, including better \ 9 | # security and more powerful configuration options. 10 | 11 | ### BEGIN INIT INFO 12 | # Provides: crond crontab 13 | # Required-Start: $local_fs $syslog 14 | # Required-Stop: $local_fs $syslog 15 | # Default-Start: 2345 16 | # Default-Stop: 90 17 | # Short-Description: run cron daemon 18 | # Description: cron is a standard UNIX program that runs user-specified 19 | # programs at periodic scheduled times. vixie cron adds a 20 | # number of features to the basic UNIX cron, including better 21 | # security and more powerful configuration options. 22 | ### END INIT INFO 23 | 24 | [ -f /etc/sysconfig/crond ] || { 25 | [ "$1" = "status" ] && exit 4 || exit 6 26 | } 27 | 28 | RETVAL=0 29 | prog="crond" 30 | exec=/usr/sbin/crond 31 | lockfile=/var/lock/subsys/crond 32 | config=/etc/sysconfig/crond 33 | 34 | # Source function library. 35 | . /etc/rc.d/init.d/functions 36 | 37 | [ $UID -eq 0 ] && [ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog 38 | 39 | start() { 40 | if [ $(id -ru) -ne 0 ] ; then 41 | echo "User has insufficient privilege." 42 | exit 4 43 | fi 44 | [ -x $exec ] || exit 5 45 | [ -f $config ] || exit 6 46 | printf "Starting $prog: " 47 | daemon $prog $CRONDARGS 48 | retval=$? 49 | echo 50 | [ $retval -eq 0 ] && touch $lockfile 51 | } 52 | 53 | stop() { 54 | if [ $(id -ru) -ne 0 ] ; then 55 | echo "User has insufficient privilege." 56 | exit 4 57 | fi 58 | printf "Stopping $prog: " 59 | if [ -n "`pidfileofproc $exec`" ]; then 60 | killproc $exec 61 | RETVAL=3 62 | else 63 | failure "Stopping $prog" 64 | fi 65 | retval=$? 66 | echo 67 | [ $retval -eq 0 ] && rm -f $lockfile 68 | } 69 | 70 | restart() { 71 | rh_status_q && stop 72 | start 73 | } 74 | 75 | reload() { 76 | printf "Reloading $prog: " 77 | if [ -n "`pidfileofproc $exec`" ]; then 78 | killproc $exec -HUP 79 | else 80 | failure "Reloading $prog" 81 | fi 82 | retval=$? 83 | echo 84 | } 85 | 86 | force_reload() { 87 | # new configuration takes effect after restart 88 | restart 89 | } 90 | 91 | rh_status() { 92 | # run checks to determine if the service is running or use generic status 93 | status -p /var/run/crond.pid $prog 94 | } 95 | 96 | rh_status_q() { 97 | rh_status >/dev/null 2>&1 98 | } 99 | 100 | 101 | case "$1" in 102 | start) 103 | rh_status_q && exit 0 104 | $1 105 | ;; 106 | stop) 107 | rh_status_q || exit 0 108 | $1 109 | ;; 110 | restart) 111 | $1 112 | ;; 113 | reload) 114 | rh_status_q || exit 7 115 | $1 116 | ;; 117 | force-reload) 118 | force_reload 119 | ;; 120 | status) 121 | rh_status 122 | ;; 123 | condrestart|try-restart) 124 | rh_status_q || exit 0 125 | restart 126 | ;; 127 | *) 128 | echo "Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload}" 129 | exit 2 130 | esac 131 | exit $? 132 | 133 | -------------------------------------------------------------------------------- /cronie_common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | /* Return: 6 | * 0 - Not found 7 | * 1 - Found */ 8 | static int find_envvar(const char *source, const char **start_pos, size_t *length) { 9 | const char *reader; 10 | size_t size = 1; 11 | int waiting_close = 0; 12 | 13 | *length = 0; 14 | *start_pos = NULL; 15 | 16 | if (source == NULL || *source == '\0') { 17 | return 0; 18 | } 19 | 20 | *start_pos = strchr(source, '$'); 21 | 22 | if (*start_pos == NULL) { 23 | return 0; 24 | } 25 | /* Skip $, since all envvars start with this char */ 26 | reader = *start_pos + 1; 27 | 28 | while (*reader != '\0') { 29 | if (*reader == '_' || isalnum(*reader)) { 30 | if (size <= 2 && isdigit(*reader)) { 31 | goto not_found; 32 | } 33 | size++; 34 | } 35 | else if (*reader == '{') { 36 | if (size != 1) { 37 | goto not_found; 38 | } 39 | size++; 40 | waiting_close = 1; 41 | } 42 | else if (*reader == '}') { 43 | if ((waiting_close && size == 2) || size == 1) { 44 | goto not_found; 45 | } 46 | 47 | if (waiting_close) { 48 | size++; 49 | } 50 | 51 | waiting_close = 0; 52 | break; 53 | } 54 | else 55 | break; 56 | 57 | reader++; 58 | } 59 | 60 | if (waiting_close) { 61 | goto not_found; 62 | } 63 | 64 | *length = size; 65 | return 1; 66 | 67 | not_found: 68 | *length = 0; 69 | *start_pos = NULL; 70 | return 0; 71 | } 72 | 73 | /* Expand env variables in 'source' arg and save to 'result' 74 | * Return: 75 | * 1 - Success 76 | * 0 - Fail */ 77 | int expand_envvar(const char *source, char *result, size_t max_size) { 78 | const char *envvar_p; 79 | size_t envvar_name_size = 0; 80 | 81 | *result = '\0'; 82 | 83 | while (find_envvar(source, &envvar_p, &envvar_name_size)) { 84 | char *envvar_name, *envvar_value; 85 | size_t prefix_size; 86 | 87 | /* Copy content before env var name */ 88 | prefix_size = envvar_p - source; 89 | 90 | if (prefix_size > 0) { 91 | if ((strlen(result) + prefix_size + 1) > max_size) { 92 | goto too_big; 93 | } 94 | strncat(result, source, prefix_size); 95 | } 96 | 97 | /* skip envvar name */ 98 | source = envvar_p + envvar_name_size; 99 | 100 | /* copy envvar name, ignoring $, { and } chars*/ 101 | envvar_p++; 102 | envvar_name_size--; 103 | 104 | if (*envvar_p == '{') { 105 | envvar_p++; 106 | envvar_name_size = envvar_name_size - 2; 107 | } 108 | 109 | envvar_name = malloc(envvar_name_size + 1); 110 | if (envvar_name == NULL) 111 | goto too_big; 112 | 113 | strncpy(envvar_name, envvar_p, envvar_name_size); 114 | envvar_name[envvar_name_size] = '\0'; 115 | 116 | /* Copy envvar value to result */ 117 | envvar_value = getenv(envvar_name); 118 | free(envvar_name); 119 | 120 | if (envvar_value != NULL) { 121 | if ((strlen(result) + strlen(envvar_value) + 1) > max_size) { 122 | goto too_big; 123 | } 124 | strcat(result, envvar_value); 125 | } 126 | } 127 | 128 | /* Copy any character left in the source string */ 129 | if (*source != '\0') { 130 | if ((strlen(result) + strlen(source) + 1) > max_size) { 131 | goto too_big; 132 | } 133 | strcat(result, source); 134 | } 135 | 136 | return 1; 137 | 138 | too_big: 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /cronie_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Copyright Red Hat Software 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 14 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* Collection of definitions, inline functions, etc, that are useful for 18 | * both cron and anacron. */ 19 | 20 | #ifndef CRONIE_COMMON_H 21 | #define CRONIE_COMMON_H 22 | 23 | #ifndef __attribute__ 24 | # if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) 25 | # define __attribute__(x) /* empty */ 26 | # endif 27 | #endif 28 | 29 | #ifndef ATTRIBUTE_NORETURN 30 | # define ATTRIBUTE_NORETURN __attribute__ ((__noreturn__)) 31 | #endif 32 | 33 | #ifndef ATTRIBUTE_UNUSED 34 | # define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) 35 | #endif 36 | 37 | #ifndef MAX_EMAILSTR 38 | #define MAX_EMAILSTR 255 /* max length of email address strings (254 + \0) */ 39 | #endif 40 | 41 | int expand_envvar(const char *, char *, size_t); 42 | 43 | #endif /* CRONIE_COMMON_H */ 44 | -------------------------------------------------------------------------------- /man/Makemodule.am: -------------------------------------------------------------------------------- 1 | dist_man_MANS = \ 2 | man/cron.8 \ 3 | man/crond.8 \ 4 | man/cronnext.1 \ 5 | man/crontab.1 \ 6 | man/crontab.5 7 | 8 | anacron_man = \ 9 | man/anacrontab.5 \ 10 | man/anacron.8 11 | 12 | EXTRA_DIST += $(anacron_man) 13 | 14 | if ANACRON 15 | dist_man_MANS += $(anacron_man) 16 | endif 17 | -------------------------------------------------------------------------------- /man/anacron.8: -------------------------------------------------------------------------------- 1 | .TH ANACRON 8 2012-11-22 "cronie" "System Administration" 2 | .SH NAME 3 | anacron \- runs commands periodically 4 | .SH SYNOPSIS 5 | .B anacron \fR[\fB-s\fR] [\fB-f\fR] [\fB-n\fR] [\fB-d\fR] [\fB-q\fR] 6 | [\fB-t anacrontab\fR] [\fB-S spooldir\fR] [\fIjob\fR] 7 | .br 8 | .B anacron \fR[\fB-S spooldir\fR] -u [\fB-t anacrontab\fR] \fR[\fIjob\fR] 9 | .br 10 | .B anacron \fR[\fB-V\fR|\fB-h\fR] 11 | .br 12 | .B anacron -T \fR[\fB-t anacrontab\fR] 13 | .SH DESCRIPTION 14 | .B Anacron 15 | is used to execute commands periodically, with a frequency specified in 16 | days. Unlike 17 | .BR cron(8) , 18 | it does not assume that the machine is running continuously. Hence, it 19 | can be used on machines that are not running 24 hours a day to control 20 | regular jobs as daily, weekly, and monthly jobs. 21 | .PP 22 | Anacron reads a list of jobs from the 23 | .I /etc/anacrontab 24 | configuration file (see 25 | .BR anacrontab (5)). 26 | This file contains the list of jobs that Anacron controls. Each job 27 | entry specifies a period in days, a delay in minutes, a unique job 28 | identifier, and a shell command. 29 | .PP 30 | For each job, Anacron checks whether this job has been executed in the 31 | last 32 | .B n 33 | days, where 34 | .B n 35 | is the time period specified for that job. If a job has not been 36 | executed in 37 | .B n 38 | days or more, Anacron runs the job's shell command, after waiting for the 39 | number of minutes specified as the delay parameter. 40 | .PP 41 | After the command exits, Anacron records the date (excludes the hour) in 42 | a special timestamp file for that job, so it knows when to execute that 43 | job again. 44 | .PP 45 | When there are no more jobs to be run, Anacron exits. 46 | .PP 47 | Anacron only considers jobs whose identifier, as specified in 48 | .BR anacrontab (5), 49 | matches any of the 50 | .I job 51 | command-line arguments. The 52 | .I job 53 | command-line arguments can be represented by shell wildcard patterns (be 54 | sure to protect them from your shell with adequate quoting). Specifying 55 | no 56 | .I job 57 | command-line arguments is equivalent to specifying "*" (that is, all 58 | jobs are considered by Anacron). 59 | .PP 60 | Unless Anacron is run with the 61 | .B \-d 62 | option (specified below), it forks to the background when it starts, and 63 | any parent processes exit immediately. 64 | .PP 65 | Unless Anacron is run with the 66 | .B \-s 67 | or 68 | .B \-n 69 | options, it starts jobs immediately when their delay is over. The 70 | execution of different jobs is completely independent. 71 | .PP 72 | If an executed job generates any output to standard output or to standard 73 | error, the output is mailed to the user under whom Anacron is running 74 | (usually root), or to the address specified in the 75 | .B MAILTO 76 | environment variable in the 77 | .I /etc/anacrontab 78 | file, if such exists. If the 79 | .B LOGNAME 80 | environment variable is set, it is used in the From: field of the mail. 81 | .PP 82 | Any informative messages generated by Anacron are sent to 83 | .BR syslogd (8) 84 | or 85 | .BR rsyslogd (8) 86 | under with facility set to 87 | .B cron 88 | and priority set to 89 | .BR notice . 90 | Any error messages are sent with the priority 91 | .BR error . 92 | .PP 93 | "Active" jobs (i.e., jobs that Anacron already decided to run and are now 94 | waiting for their delay to pass, and jobs that are currently being 95 | executed by Anacron), are "locked", so that other copies of Anacron 96 | cannot run them at the same time. 97 | .SH OPTIONS 98 | .TP 99 | .B \-f 100 | Forces execution of all jobs, ignoring any timestamps. 101 | .TP 102 | .B \-u 103 | Updates the timestamps of all jobs to the current date, but does not run 104 | any. 105 | .TP 106 | .B \-s 107 | Serializes execution of jobs. Anacron does not start a new job before the 108 | previous one finished. 109 | .TP 110 | .B \-n 111 | Runs jobs immediately and ignores the specified delays in the 112 | .I /etc/anacrontab 113 | file. This options implies 114 | .BR -s . 115 | .TP 116 | .B \-d 117 | Does not fork Anacron to the background. In this mode, Anacron will 118 | output informational messages to standard error, as well as to syslog. 119 | The output of any job is mailed by Anacron. 120 | .TP 121 | .B \-q 122 | Suppresses any messages to standard error. Only applicable with 123 | .BR -d . 124 | .TP 125 | .B -t some_anacrontab 126 | Uses the specified anacrontab, rather than the 127 | .I /etc/anacrontab 128 | default one. 129 | .TP 130 | .B -T 131 | Anacrontab testing. Tests the 132 | .I /etc/anacrontab 133 | configuration file for validity. If there is an error in the file, it is 134 | shown on the standard output and Anacron returns the value of 1. Valid 135 | anacrontabs return the value of 0. 136 | .TP 137 | .B -S spooldir 138 | Uses the specified spooldir to store timestamps in. This option is 139 | required for users who wish to run anacron themselves. 140 | .TP 141 | .B -V 142 | Prints version information, and exits. 143 | .TP 144 | .B -h 145 | Prints short usage message, and exits. 146 | .SH SIGNALS 147 | After receiving a 148 | .B SIGUSR1 149 | signal, Anacron waits for any running jobs to finish and then exits. 150 | This can be used to stop Anacron cleanly. 151 | .SH NOTES 152 | Make sure your time-zone is set correctly before Anacron is started since 153 | the time-zone affects the date. This is usually accomplished by setting 154 | the TZ environment variable, or by installing a 155 | .I /usr/lib/zoneinfo/localtime 156 | file. See 157 | .BR tzset (3) 158 | for more information. 159 | .PP 160 | Timestamp files are created in the spool directory for each job specified 161 | in an anacrontab. These files are never removed automatically by 162 | Anacron, and should be removed by hand if a job is no longer being 163 | scheduled. 164 | .SH FILES 165 | .TP 166 | .I /etc/anacrontab 167 | Contains specifications of jobs. See 168 | .BR anacrontab (5) 169 | for a complete description. 170 | .TP 171 | .I /var/spool/anacron 172 | This directory is used by Anacron for storing timestamp files. 173 | .SH "SEE ALSO" 174 | .BR anacrontab (5), 175 | .BR cron (8), 176 | .BR tzset (3) 177 | .PP 178 | The Anacron 179 | .I README 180 | file. 181 | .SH BUGS 182 | Anacron never removes timestamp files. Remove unused files manually. 183 | .PP 184 | Anacron uses up to two file descriptors for each active job. It may run 185 | out of descriptors if there are lots of active jobs. See 186 | .B echo $(($(ulimit -n) / 2)) 187 | for information how many concurrent jobs anacron may run. 188 | .PP 189 | Mail comments, suggestions and bug reports to 190 | .MT shaleh@\:(debian.\:org|\:valinux.\:com) 191 | Sean 'Shaleh' Perry 192 | .ME . 193 | .SH AUTHOR 194 | Anacron was originally conceived and implemented by 195 | .MT schwarz@\:monet.\:m.\:isar.\:de 196 | Christian Schwarz 197 | .ME . 198 | .PP 199 | The current implementation is a complete rewrite by 200 | .MT itzur@\:actcom.\:co.\:il 201 | Itai Tzur 202 | .ME . 203 | .PP 204 | The code base was maintained by 205 | .MT shaleh@\:(debian.\:org|\:valinux.\:com) 206 | Sean 'Shaleh' Perry 207 | .ME . 208 | .PP 209 | Since 2004, it is maintained by 210 | .MT pasc@\:(debian.\:org|\:redellipse.\:net) 211 | Pascal Hakim 212 | .ME . 213 | .PP 214 | For Fedora, Anacron is maintained by 215 | .MT mmaslano@redhat.\:com 216 | Marcela Mašláňová 217 | .ME . 218 | -------------------------------------------------------------------------------- /man/anacrontab.5: -------------------------------------------------------------------------------- 1 | .TH ANACRONTAB 5 2012-11-22 "cronie" "File Formats" 2 | .SH NAME 3 | /etc/anacrontab \- configuration file for Anacron 4 | .SH DESCRIPTION 5 | The 6 | .I /etc/anacrontab 7 | configuration file describes the jobs controlled by 8 | .BR anacron (8). 9 | It can contain three types of lines: job-description lines, environment 10 | assignments, or empty lines. 11 | .PP 12 | Job-description lines can have the following format: 13 | .PP 14 | period in days delay in minutes job-identifier command 15 | .PP 16 | The 17 | .I period in days 18 | variable specifies the frequency of execution of a job in days. This 19 | variable can be represented by an integer or a macro (@daily, @weekly, 20 | @monthly), where @daily denotes the same value as the integer 1, @weekly 21 | the same as 7, and @monthly specifies that the job is run once a month, 22 | independent on the length of the month. 23 | .PP 24 | The 25 | .I delay in minutes 26 | variable specifies the number of minutes anacron waits, if necessary, 27 | before executing a job. This variable is represented by an integer where 28 | 0 means no delay. 29 | .PP 30 | The 31 | .I job-identifier 32 | variable specifies a unique name of a job which is used in the log files. 33 | .PP 34 | The 35 | .I command 36 | variable specifies the command to execute. The command can either be a 37 | command such as 38 | .B ls /proc >> /tmp/proc 39 | or a command to execute a custom script. 40 | .PP 41 | Environment assignment lines can have the following format: 42 | .PP 43 | VAR=VALUE 44 | .PP 45 | Any spaces around 46 | .I VAR 47 | are removed. No spaces around 48 | .I VALUE 49 | are allowed (unless you want them to be part of the value). The 50 | specified assignment takes effect from the next line until the end of the 51 | file, or to the next assignment of the same variable. 52 | .PP 53 | The 54 | .I START_HOURS_RANGE 55 | variable defines an interval (in hours) when scheduled jobs can be run. 56 | In case this time interval is missed, for example, due to a power down, 57 | then scheduled jobs are not executed that day. 58 | .PP 59 | The 60 | .I RANDOM_DELAY 61 | variable denotes the maximum number of minutes that will be added to the 62 | delay in minutes variable which is specified for each job. A 63 | .I RANDOM_DELAY 64 | set to 12 would therefore add, randomly, between 0 and 12 minutes to the 65 | delay in minutes for each job in that particular anacrontab. When set to 66 | 0, no random delay is added. 67 | .PP 68 | If 69 | .I MAILTO 70 | is defined (and non-empty), mail is sent to the specified address, 71 | otherwise, system user is used. 72 | .PP 73 | If 74 | .I MAILFROM 75 | is defined (and non-empty), it is used as the envelope sender address, 76 | otherwise, system user is used. 77 | .PP 78 | (Note: Both 79 | .I MAILFROM 80 | and 81 | .I MAILTO 82 | variables are expanded, so setting them as in the following example works as expected: MAILFROM=cron-$USER@cron.com ($USER is replaced by the system user) ) 83 | .PP 84 | If 85 | .I NO_MAIL_OUTPUT 86 | is defined (and non-empty), the standard output and error descriptors of job processes are not redirected and e-mailed. 87 | .PP 88 | .PP 89 | Empty lines are either blank lines, line containing white spaces only, or 90 | lines with white spaces followed by a '#' followed by an arbitrary 91 | comment. 92 | .PP 93 | You can continue a line onto the next line by adding a '\\' at the end of it. 94 | .PP 95 | In case you want to disable Anacron, add a line with 96 | .I 0anacron 97 | which is the name of the script running the Anacron into the 98 | .I /etc/cron.hourly/jobs.deny 99 | file. 100 | .SH EXAMPLE 101 | This example shows how to set up an Anacron job similar in functionality to 102 | .I /etc/crontab 103 | which starts all regular jobs 104 | between 6:00 and 8:00 105 | .I only. 106 | A 107 | .I RANDOM_DELAY 108 | which can be 30 minutes at the most is specified. Jobs will run 109 | serialized in a queue where each job is started only after the previous 110 | one is finished. 111 | .PP 112 | .nf 113 | # environment variables 114 | SHELL=/bin/sh 115 | PATH=/sbin:/bin:/usr/sbin:/usr/bin 116 | MAILTO=root 117 | RANDOM_DELAY=30 118 | # Anacron jobs will start between 6am and 8am. 119 | START_HOURS_RANGE=6-8 120 | # delay will be 5 minutes + RANDOM_DELAY for cron.daily 121 | 1 5 cron.daily nice run-parts /etc/cron.daily 122 | 7 0 cron.weekly nice run-parts /etc/cron.weekly 123 | @monthly 0 cron.monthly nice run-parts /etc/cron.monthly 124 | .fi 125 | .SH "SEE ALSO" 126 | .BR anacron (8), 127 | .BR crontab (1) 128 | .PP 129 | The Anacron 130 | .I README 131 | file. 132 | .SH AUTHOR 133 | .MT itzur@\:actcom.\:co.\:il 134 | Itai Tzur 135 | .ME 136 | .PP 137 | Currently maintained by 138 | .MT pasc@\:(debian.\:org|\:redellipse.\:net) 139 | Pascal Hakim 140 | .ME . 141 | .PP 142 | For Fedora, maintained by 143 | .MT mmaslano@redhat.com 144 | Marcela Mašláňová 145 | .ME . 146 | -------------------------------------------------------------------------------- /man/cron.8: -------------------------------------------------------------------------------- 1 | .\"/* Copyright 1988,1990,1993,1996 by Paul Vixie 2 | .\" * All rights reserved 3 | .\" */ 4 | .\" 5 | .\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 6 | .\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 7 | .\" 8 | .\" Permission to use, copy, modify, and distribute this software for any 9 | .\" purpose with or without fee is hereby granted, provided that the above 10 | .\" copyright notice and this permission notice appear in all copies. 11 | .\" 12 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 13 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 15 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 18 | .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 | .\" 20 | .\" Modified 2010/09/12 by Colin Dean, Durham University IT Service, 21 | .\" to add clustering support. 22 | .\" 23 | .\" $Id: cron.8,v 1.8 2004/01/23 19:03:32 vixie Exp $ 24 | .\" 25 | .TH CRON "8" "2013-09-26" "cronie" "System Administration" 26 | .SH NAME 27 | crond \- daemon to execute scheduled commands 28 | .SH SYNOPSIS 29 | .B crond 30 | .RB [ -c " | " -h " | " -i " | " -n " | " -p " | " -P " | " -s " | " -m \fP\fI\fP ] 31 | .br 32 | .B crond 33 | .B -x 34 | .RB [ext,sch,proc,pars,load,misc,test,bit] 35 | .br 36 | .B crond 37 | .B -V 38 | .SH DESCRIPTION 39 | .I Cron 40 | is started from 41 | .I /etc/rc.d/init.d 42 | or 43 | .I /etc/init.d 44 | when classical sysvinit scripts are used. In case systemd is enabled, then unit file is installed into 45 | .I /lib/systemd/system/crond.service 46 | and daemon is started by 47 | .I systemctl start crond.service 48 | command. It returns immediately, thus, there is no need to need to start it with 49 | the '&' parameter. 50 | .PP 51 | .I Cron 52 | searches 53 | .I /var/spool/cron 54 | for crontab files which are named after accounts in 55 | .I /etc/passwd; 56 | The found crontabs are loaded into the memory. 57 | .I Cron 58 | also searches for 59 | .I /etc/anacrontab 60 | and any files in the 61 | .I /etc/cron.d 62 | directory, which have a different format (see 63 | .BR crontab (5)). 64 | .I Cron 65 | examines all stored crontabs and checks each job to see if it needs to be 66 | run in the current minute. When executing commands, any output is mailed 67 | to the owner of the crontab (or to the user specified in the 68 | .I MAILTO 69 | environment variable in the crontab, if such exists). Any job output can 70 | also be sent to syslog by using the 71 | .B "\-s" 72 | option. 73 | .PP 74 | There are two ways how changes in crontables are checked. The first 75 | method is checking the modtime of a file. The second method is using the 76 | inotify support. Using of inotify is logged in the 77 | .I /var/log/cron 78 | log after the daemon is started. The inotify support checks for changes 79 | in all crontables and accesses the hard disk only when a change is 80 | detected. 81 | .PP 82 | When using the modtime option, 83 | .I Cron 84 | checks its crontables' modtimes every minute to check for any changes and 85 | reloads the crontables which have changed. There is no need to restart 86 | .I Cron 87 | after some of the crontables were modified. The modtime option is also 88 | used when inotify can not be initialized. 89 | .PP 90 | .I Cron 91 | checks these files and directories: 92 | .TP 93 | .IR /etc/crontab 94 | system crontab. Nowadays the file is empty by default. Originally it 95 | was usually used to run daily, weekly, monthly jobs. By default these 96 | jobs are now run through anacron which reads 97 | .IR /etc/anacrontab 98 | configuration file. See 99 | .BR anacrontab (5) 100 | for more details. 101 | .TP 102 | .IR /etc/cron.d/ 103 | directory that contains system cronjobs stored for different users. 104 | .TP 105 | .IR /var/spool/cron 106 | directory that contains user crontables created by the 107 | .IR crontab 108 | command. 109 | .PP 110 | Note that the 111 | .BR crontab (1) 112 | command updates the modtime of the spool directory whenever it changes a 113 | crontab. 114 | .PP 115 | .SS Daylight Saving Time and other time changes 116 | Local time changes of less than three hours, such as those caused by the 117 | Daylight Saving Time changes, are handled in a special way. This only 118 | applies to jobs that run at a specific time and jobs that run with a 119 | granularity greater than one hour. Jobs that run more frequently are 120 | scheduled normally. 121 | .PP 122 | If time was adjusted one hour forward, those jobs that would have run in 123 | the interval that has been skipped will be run immediately. Conversely, 124 | if time was adjusted backward, running the same job twice is avoided. 125 | .PP 126 | Time changes of more than 3 hours are considered to be corrections to the 127 | clock or the timezone, and the new time is used immediately. 128 | .PP 129 | It is possible to use different time zones for crontables. See 130 | .BR crontab (5) 131 | for more information. 132 | .SS PAM Access Control 133 | .IR Cron 134 | supports access control with PAM if the system has PAM installed. For 135 | more information, see 136 | .BR pam (8). 137 | A PAM configuration file for 138 | .IR crond 139 | is installed in 140 | .IR /etc/pam.d/crond . 141 | The daemon loads the PAM environment from the pam_env module. This can 142 | be overridden by defining specific settings in the appropriate crontab 143 | file. 144 | .SH "OPTIONS" 145 | .TP 146 | .B "\-h" 147 | Prints a help message and exits. 148 | .TP 149 | .B "\-i" 150 | Disables inotify support. 151 | .TP 152 | .B "\-m" 153 | This option allows you to specify a shell command to use for sending 154 | .I Cron 155 | mail output instead of using 156 | .BR sendmail (8) 157 | This command must accept a fully formatted mail message (with headers) on 158 | standard input and send it as a mail message to the recipients specified 159 | in the mail headers. Specifying the string 160 | .I "off" 161 | (i.e., crond -m off) 162 | will disable the sending of mail. 163 | .TP 164 | .B "\-n" 165 | Tells the daemon to run in the foreground. This can be useful when 166 | starting it out of init. With this option is needed to change pam setting. 167 | .I /etc/pam.d/crond 168 | must not enable 169 | .I pam_loginuid.so 170 | module. 171 | .TP 172 | .B "\-f" 173 | the same as -n, consistent with other crond implementations. 174 | .TP 175 | .B "\-p" 176 | Allows 177 | .I Cron 178 | to accept any user set crontables. 179 | .TP 180 | .B "\-P" 181 | Don't set PATH. PATH is instead inherited from the environment. 182 | .TP 183 | .B "\-c" 184 | This option enables clustering support, as described below. 185 | .TP 186 | .B "\-s" 187 | This option will direct 188 | .I Cron 189 | to send the job output to the system log using 190 | .BR syslog (3). 191 | This is useful if your system does not have 192 | .BR sendmail (8) 193 | installed or if mail is disabled. 194 | .TP 195 | .B "\-x" 196 | This option allows you to set debug flags. 197 | .TP 198 | .B "\-V" 199 | Print version and exit. 200 | .SH SIGNALS 201 | When the 202 | .I SIGHUP 203 | is received, the 204 | .I Cron 205 | daemon will close and reopen its log file. This proves to be useful in 206 | scripts which rotate and age log files. Naturally, this is not relevant 207 | if 208 | .I Cron 209 | was built to use 210 | .IR syslog (3). 211 | .SH CLUSTERING SUPPORT 212 | In this version of 213 | .IR Cron 214 | it is possible to use a network-mounted shared 215 | .I /var/spool/cron 216 | across a cluster of hosts and specify that only one of the hosts should 217 | run the crontab jobs in this directory at any one time. This is done by 218 | starting 219 | .I Cron 220 | with the 221 | .B \-c 222 | option, and have the 223 | .I /var/spool/cron/.cron.hostname 224 | file contain just one line, which represents the hostname of whichever 225 | host in the cluster should run the jobs. If this file does not exist, or 226 | the hostname in it does not match that returned by 227 | .BR gethostname (2), 228 | then all crontab files in this directory are ignored. This has no effect 229 | on cron jobs specified in the 230 | .I /etc/crontab 231 | file or on files in the 232 | .I /etc/cron.d 233 | directory. These files are always run and considered host-specific. 234 | .PP 235 | Rather than editing 236 | .I /var/spool/cron/.cron.hostname 237 | directly, use the 238 | .B \-n 239 | option of 240 | .BR crontab (1) 241 | to specify the host. 242 | .PP 243 | You should ensure that all hosts in a cluster, and the file server from 244 | which they mount the shared crontab directory, have closely synchronised 245 | clocks, e.g., using 246 | .BR ntpd (8), 247 | otherwise the results will be very unpredictable. 248 | .PP 249 | Using cluster sharing automatically disables inotify support, because 250 | inotify cannot be relied on with network-mounted shared file systems. 251 | .SH CAVEATS 252 | All 253 | .BR crontab 254 | files have to be regular files or symlinks to regular files, they must 255 | not be executable or writable for anyone else but the owner. This 256 | requirement can be overridden by using the 257 | .B \-p 258 | option on the crond command line. If inotify support is in use, changes 259 | in the symlinked crontabs are not automatically noticed by the cron 260 | daemon. The cron daemon must receive a SIGHUP signal to reload the 261 | crontabs. This is a limitation of the inotify API. 262 | .PP 263 | The syslog output will be used instead of mail, when sendmail is not 264 | installed. 265 | .SH "SEE ALSO" 266 | .BR crontab (1), 267 | .BR crontab (5), 268 | .BR inotify (7), 269 | .BR pam (8) 270 | .SH AUTHOR 271 | .MT vixie@isc.org 272 | Paul Vixie 273 | .ME 274 | .br 275 | .MT mmaslano@redhat.com 276 | Marcela Mašláňová 277 | .ME 278 | .br 279 | .MT colin@colin-dean.org 280 | Colin Dean 281 | .ME 282 | .br 283 | .MT tmraz@fedoraproject.org 284 | Tomáš Mráz 285 | .ME 286 | -------------------------------------------------------------------------------- /man/crond.8: -------------------------------------------------------------------------------- 1 | .so man8/cron.8 2 | -------------------------------------------------------------------------------- /man/cronnext.1: -------------------------------------------------------------------------------- 1 | .TH CRONNEXT 1 "2017-06-11" "cronie" "User Commands" 2 | .SH NAME 3 | cronnext \- time of next job cron will execute 4 | .SH SYNOPSIS 5 | .TP 9 6 | .B cronnext 7 | [\fB-i \fIusers\fR] [\fB-e \fIusers\fR] [\fB-s\fR] 8 | [\fB-a\fR] 9 | [\fB-t \fItime\fR] [\fB-q \fItime\fR] [\fB-j \fIcommand\fR] 10 | [\fB-l\fR] [\fB-c\fR] [\fB-f\fR] [\fB-h\fR] [\fB-V\fR] 11 | [file]... 12 | .SH DESCRIPTION 13 | Determine the time cron will execute the next job. Without arguments, it 14 | prints that time considering all crontabs, in number of seconds since the 15 | Epoch, rounded to the minute. This number can be converted into other formats 16 | using 17 | .BR date (1), 18 | like 19 | .B date --date @43243254 20 | 21 | The file arguments are optional. If provided, 22 | .I cronnext 23 | uses them as crontabs instead of the ones installed in the system. 24 | .SH OPTIONS 25 | .TP 26 | .BI "\-i " user,user,user,... 27 | Consider only the crontabs of the specified users. Use 28 | .B *system* 29 | for the system crontab. 30 | .TP 31 | .BI "\-e " user,user,user,... 32 | Do not consider the crontabs of the specified users. 33 | .TP 34 | .B \-s 35 | Do not consider the system crontab, usually the 36 | .I /etc/crontab 37 | file. The system crontab usually contains the hourly, daily, weekly and 38 | monthly crontabs, which might be better dealt with 39 | .BR anacron (8). 40 | .TP 41 | .BI \-a 42 | Use the crontabs installed in the system in addition to the ones passed as 43 | file arguments. This is implicit if no file is passed. 44 | .TP 45 | .BI "\-t " time 46 | Determine the next job from this time, instead of now. The time is 47 | expressed in number of seconds since the Epoch, as obtained for example by 48 | .BR "date +%s --date \(dqnow + 2 hours\(dq" , 49 | and is internally rounded to the minute. 50 | .TP 51 | .BI "\-q " time 52 | Do not check jobs over this time, expressed in the same way as in option 53 | .BR -t . 54 | .TP 55 | .BI "\-j " command 56 | Only look for jobs that contain \fIcommand\fP as a substring. 57 | .TP 58 | .B \-l 59 | Print the whole entries of the jobs that are the next to be executed by cron. 60 | The default is to only print their next time of execution. 61 | .TP 62 | .B \-c 63 | Print every entry in every crontab with the next time it is executed. 64 | .TP 65 | .B \-f 66 | Print all jobs that are executed in the given interval. Requires option 67 | \fB-q\fR. 68 | .TP 69 | .B \-h 70 | Print usage output and exit. 71 | .TP 72 | .B \-V 73 | Print version and exit. 74 | .SH AUTHOR 75 | .MT sgerwk@aol.com 76 | Marco Migliori 77 | .ME 78 | .SH SEE ALSO 79 | .BR cron (8), 80 | .BR cron (1), 81 | .BR crontab (5), 82 | .BR crontab (1), 83 | .BR anacron (8), 84 | .BR anacrontab (5), 85 | .BR atq (1), 86 | .BR date (1) 87 | -------------------------------------------------------------------------------- /man/crontab.1: -------------------------------------------------------------------------------- 1 | .\"/* Copyright 1988,1990,1993 by Paul Vixie 2 | .\" * All rights reserved 3 | .\" */ 4 | .\" 5 | .\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 6 | .\" Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 7 | .\" 8 | .\" Permission to use, copy, modify, and distribute this software for any 9 | .\" purpose with or without fee is hereby granted, provided that the above 10 | .\" copyright notice and this permission notice appear in all copies. 11 | .\" 12 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 13 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 15 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 18 | .\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 | .\" 20 | .\" Modified 2010/09/12 by Colin Dean, Durham University IT Service, 21 | .\" to add clustering support. 22 | .\" 23 | .\" $Id: crontab.1,v 1.7 2004/01/23 19:03:32 vixie Exp $ 24 | .\" 25 | .TH CRONTAB 1 "2019-10-29" "cronie" "User Commands" 26 | .SH NAME 27 | crontab \- maintains crontab files for individual users 28 | .SH SYNOPSIS 29 | .B crontab 30 | .RB [ -u 31 | .IR user ] 32 | .RI < "file" 33 | .RB | \ - > 34 | .br 35 | .B crontab 36 | .RB [ -T ] 37 | .RI < "file" 38 | .RB | \ - > 39 | .br 40 | .B crontab 41 | .RB [ -u 42 | .IR user ] 43 | .RB < -l " | " -r " | " -e >\ [ -i ] 44 | .RB [ -s ] 45 | .br 46 | .B crontab 47 | .BR -n \ [ 48 | .IR "hostname " ] 49 | .br 50 | .B crontab 51 | .BR -c 52 | .br 53 | .B crontab 54 | .BR -V 55 | .SH DESCRIPTION 56 | .I Crontab 57 | is the program used to install a crontab table 58 | .IR file , 59 | remove or list the existing tables used to serve the 60 | .BR cron (8) 61 | daemon. Each user can have their own crontab, and though these are files 62 | in 63 | .IR /var/spool/ , 64 | they are not intended to be edited directly. For SELinux in MLS mode, 65 | you can define more crontabs for each range. For more information, see 66 | .BR selinux (8). 67 | .PP 68 | In this version of 69 | .IR Cron 70 | it is possible to use a network-mounted shared 71 | .I /var/spool/cron 72 | across a cluster of hosts and specify that only one of the hosts should 73 | run the crontab jobs in the particular directory at any one time. You 74 | may also use 75 | .BR crontab 76 | from any of these hosts to edit the same shared set of crontab files, and 77 | to set and query which host should run the crontab jobs. 78 | .PP 79 | Scheduling cron jobs with 80 | .BR crontab 81 | can be allowed or disallowed for different users. For this purpose, use the 82 | .I cron.allow 83 | and 84 | .I cron.deny 85 | files. If the 86 | .I cron.allow 87 | file exists, a user must be listed in it to be allowed to use 88 | .BR crontab . 89 | If the 90 | .I cron.allow 91 | file does not exist but the 92 | .I cron.deny 93 | file does exist, then a user must 94 | .I not 95 | be listed in the 96 | .I cron.deny 97 | file in order to use 98 | .BR crontab. 99 | If neither of these files exist, then only the super user is allowed to use 100 | .BR crontab . 101 | .PP 102 | Another way to restrict the scheduling of cron jobs beyond 103 | .BR crontab 104 | is to use PAM authentication in 105 | .I /etc/security/access.conf 106 | to set up users, which are allowed or disallowed to use 107 | .BR crontab 108 | or modify system cron jobs in the 109 | .IR /etc/cron.d/ 110 | directory. 111 | .PP 112 | The temporary directory can be set in an environment variable. If it is 113 | not set by the user, the 114 | .I /tmp 115 | directory is used. 116 | .PP 117 | When listing a crontab on a terminal the output will be colorized unless 118 | an environment variable 119 | .I NO_COLOR 120 | is set. 121 | .PP 122 | On edition or deletion of the crontab, a backup of the last crontab will be saved to 123 | .I $XDG_CACHE_HOME/crontab/crontab.bak 124 | or 125 | .I $XDG_CACHE_HOME/crontab/crontab..bak 126 | if 127 | .B -u 128 | is used. 129 | If the 130 | .I XDG_CACHE_HOME 131 | environment variable is not set, 132 | .I $HOME/.cache 133 | will be used instead. 134 | .PP 135 | .SH "OPTIONS" 136 | .TP 137 | .B "\-u" 138 | Specifies the name of the user whose crontab is to be modified. If this 139 | option is not used, 140 | .BR crontab 141 | examines "your" crontab, i.e., the crontab of the person executing the 142 | command. If no crontab exists for a particular user, it is created for 143 | them the first time the 144 | .B crontab -u 145 | command is used under their username. 146 | .TP 147 | .B "\-T" 148 | Test the crontab file syntax without installing it. 149 | Once an issue is found, the validation is interrupted, so this will not return all the existing issues at the same execution. 150 | .TP 151 | .B "\-l" 152 | Displays the current crontab on standard output. 153 | .TP 154 | .B "\-r" 155 | Removes the current crontab. 156 | .TP 157 | .B "\-e" 158 | Edits the current crontab using the editor specified by the 159 | .I VISUAL 160 | or 161 | .I EDITOR 162 | environment variables. After you exit from the editor, the modified 163 | crontab will be installed automatically. 164 | .TP 165 | .B "\-i" 166 | This option modifies the 167 | .B "\-r" 168 | option to prompt the user for a 'y/Y' response before actually removing 169 | the crontab. 170 | .TP 171 | .B "\-s" 172 | Appends the current SELinux security context string as an MLS_LEVEL 173 | setting to the crontab file before editing / replacement occurs - see the 174 | documentation of MLS_LEVEL in 175 | .BR crontab (5). 176 | .TP 177 | .B "\-n" 178 | This option is relevant only if 179 | .BR cron (8) 180 | was started with the 181 | .B \-c 182 | option, to enable clustering support. It is used to set the host in the 183 | cluster which should run the jobs specified in the crontab files in the 184 | .I /var/spool/cron 185 | directory. If a hostname is supplied, the host whose hostname returned 186 | by 187 | .BR gethostname (2) 188 | matches the supplied hostname, will be selected to run the selected cron jobs subsequently. If there 189 | is no host in the cluster matching the supplied hostname, or you explicitly specify 190 | an empty hostname, then the selected jobs will not be run at all. If the hostname 191 | is omitted, the name of the local host returned by 192 | .BR gethostname (2) 193 | is used. Using this option has no effect on the 194 | .I /etc/crontab 195 | file and the files in the 196 | .I /etc/cron.d 197 | directory, which are always run, and considered host-specific. For more 198 | information on clustering support, see 199 | .BR cron (8). 200 | .TP 201 | .B "\-c" 202 | This option is only relevant if 203 | .BR cron (8) 204 | was started with the 205 | .B \-c 206 | option, to enable clustering support. It is used to query which host in 207 | the cluster is currently set to run the jobs specified in the crontab 208 | files in the directory 209 | .I /var/spool/cron 210 | , as set using the 211 | .B \-n 212 | option. 213 | .TP 214 | .B "\-V" 215 | Print version and exit. 216 | .SH CAVEATS 217 | The files 218 | .I cron.allow 219 | and 220 | .I cron.deny 221 | cannot be used to restrict the execution of cron jobs; they only restrict the 222 | use of 223 | .BR crontab . 224 | In particular, restricting access to 225 | .BR crontab 226 | has no effect on an existing 227 | .I crontab 228 | of a user. Its jobs will continue to be executed until the crontab is removed. 229 | .PP 230 | The files 231 | .I cron.allow 232 | and 233 | .I cron.deny 234 | must be readable by the user invoking 235 | .BR crontab . 236 | If this is not the case, then they are treated as non-existent. 237 | .SH "SEE ALSO" 238 | .BR crontab (5), 239 | .BR cron (8) 240 | .SH FILES 241 | .nf 242 | /etc/cron.allow 243 | /etc/cron.deny 244 | .fi 245 | .SH STANDARDS 246 | The 247 | .I crontab 248 | command conforms to IEEE Std1003.2-1992 (``POSIX'') with one exception: 249 | For replacing the current crontab with data from standard input the 250 | .B \- 251 | has to be specified on the command line if the standard input is a TTY. 252 | This new command syntax differs from previous versions of Vixie Cron, 253 | as well as from the classic SVR3 syntax. 254 | .SH DIAGNOSTICS 255 | An informative usage message appears if you run a crontab with a faulty 256 | command defined in it. 257 | .SH AUTHOR 258 | .MT vixie@isc.org 259 | Paul Vixie 260 | .ME 261 | .br 262 | .MT colin@colin-dean.org 263 | Colin Dean 264 | .ME 265 | -------------------------------------------------------------------------------- /pam/crond: -------------------------------------------------------------------------------- 1 | # 2 | # The PAM configuration file for the cron daemon 3 | # 4 | # 5 | # Although no PAM authentication is called, auth modules 6 | # are used for credential setting 7 | auth include system-auth 8 | account required pam_access.so 9 | account include system-auth 10 | session required pam_loginuid.so 11 | session include system-auth 12 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Cronie 2 | Cronie contains the standard UNIX daemon crond that runs specified programs at 3 | scheduled times and related tools. The source is based on the original vixie-cron 4 | and has security and configuration enhancements like the ability to use pam and 5 | SELinux. 6 | 7 | And why cronie? [http://www.urbandictionary.com/define.php?term=cronie] 8 | 9 | # Download 10 | Latest released version is 1.7.2. 11 | 12 | User visible changes: 13 | 14 | Release 1.7.2 15 | 16 | - crond: Revert setting the return path to <>. It is not RFC compliant. 17 | - crond: Inherit MAILFROM from the crond process environment. 18 | 19 | Release 1.7.1 20 | 21 | - crond: Wait on finishing the job with -n option to check 22 | the exit status 23 | - crond: Do not set the return path to <> if non-default MAILFROM is set 24 | - /etc/sysconfig/crond and /etc/default/anacron files are optional 25 | 26 | Release 1.7.0 27 | 28 | - anacron: Add support for NO_MAIL_OUTPUT environment variable 29 | - anacron: Support enabling anacron jobs on battery power 30 | - crond: Support -n crontab entry option to disable mailing the output 31 | - crontab: Make a backup of the crontab file on edition and deletion 32 | 33 | Release 1.6.1 34 | 35 | - crond: Fix regression of handling ranges (x-y) in crontab 36 | 37 | Release 1.6.0 38 | 39 | - crond: Add switch -f as an alias for -n 40 | - crond: Add random within range '~' operator 41 | - crond: Use the configure runstatedir directory for pid file 42 | - crond: Increase the maximum number of crontab entries to 10000 43 | 44 | Release 1.5.7 45 | 46 | - anacron: Fix problem of anacron not being started on some desktops 47 | - crontab: switch off colors if NO_COLOR is set 48 | 49 | Release 1.5.6 50 | 51 | - crontab: crontab without arguments now works if stdin is not a TTY 52 | - crond: Fix various issues on loading the crontab databases on startup 53 | - anacron: Expand MAILTO and MAILFROM environment variables 54 | - crontab: New option to test crontab file syntax without installing it 55 | 56 | Release 1.5.5 57 | - Explicitly validate upper end of range and step to disallow entries 58 | such as: 1-234/5678 * * * * .... 59 | - crond: Report missing newline before EOF in syslog so the line is not 60 | completely silently ignored. 61 | - crontab -l colors comment lines in a different color. 62 | - crond: Revert "Avoid creating pid files when crond doesn't fork". 63 | - anacron is built by default. 64 | - Use non-recursive build. 65 | - cronnext: Allow to optionally select jobs by substring. 66 | 67 | Release 1.5.4 68 | - crond: Fix regression from previous release. Only first job from a crontab 69 | was being run. 70 | 71 | Release 1.5.3 72 | - Fix CVE-2019-9704 and CVE-2019-9705 to avoid local DoS of the crond. 73 | - crontab: Make crontab without arguments fail. 74 | - crond: In PAM configuration include system-auth instead of password-auth. 75 | - crond: In the systemd service file restart crond if it fails. 76 | - crond: Use the role from the crond context for system job contexts. 77 | - Multiple small cleanups and fixes. 78 | 79 | The source can be downloaded from [https://github.com/cronie-crond/cronie/releases] 80 | 81 | Cronie is packaged by these distributions: 82 | - Fedora [https://apps.fedoraproject.org/packages/cronie] 83 | - Mandriva [http://sophie.zarb.org/srpm/Mandriva,cooker,/cronie/history] 84 | - Gentoo [http://packages.gentoo.org/package/sys-process/cronie] 85 | - Source Mage [http://dbg.download.sourcemage.org/grimoire/codex/stable/utils/cronie/] 86 | - openSUSE [https://software.opensuse.org/package/cronie] 87 | - Arch Linux [https://www.archlinux.org/packages/core/x86_64/cronie/] 88 | - Void Linux [https://github.com/void-linux/void-packages/tree/master/srcpkgs/cronie] 89 | 90 | # Contact 91 | 92 | Mailing list: `cronie-devel AT lists.fedorahosted DOT org` 93 | 94 | To report bugs please use the github issue tracker of this project. 95 | -------------------------------------------------------------------------------- /src/.dirstamp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cronie-crond/cronie/e6c2853856c3103a4add4c3673b3306cc21d341e/src/.dirstamp -------------------------------------------------------------------------------- /src/.indent.pro: -------------------------------------------------------------------------------- 1 | --blank-before-sizeof 2 | --brace-indent0 3 | --braces-on-func-def-line 4 | --braces-on-if-line 5 | --braces-on-struct-decl-line 6 | --break-after-boolean-operator 7 | --case-brace-indentation0 8 | --case-indentation0 9 | --comment-indentation0 10 | --continuation-indentation4 11 | --cuddle-do-while 12 | --declaration-comment-column0 13 | --declaration-indentation0 14 | --dont-break-function-decl-args 15 | --dont-break-procedure-type 16 | --dont-line-up-parentheses 17 | --honour-newlines 18 | --indent-level4 19 | --line-length80 20 | --paren-indentation4 21 | --preprocessor-indentation1 22 | --no-blank-lines-after-commas 23 | --space-after-cast 24 | --space-after-for 25 | --space-after-if 26 | --space-after-while 27 | --space-special-semicolon 28 | --no-space-after-parentheses 29 | --no-space-after-function-call-names 30 | --start-left-side-of-comments 31 | --struct-brace-indentation4 32 | --tab-size4 33 | -------------------------------------------------------------------------------- /src/Makemodule.am: -------------------------------------------------------------------------------- 1 | # Makefile.am - two binaries crond and crontab 2 | 3 | sbin_PROGRAMS += \ 4 | src/crond 5 | 6 | bin_PROGRAMS += \ 7 | src/cronnext \ 8 | src/crontab 9 | 10 | src_crond_SOURCES = \ 11 | src/cron.c \ 12 | src/database.c \ 13 | src/do_command.c \ 14 | src/job.c \ 15 | src/popen.c \ 16 | src/security.c \ 17 | src/user.c \ 18 | cronie_common.c \ 19 | $(common_src) 20 | 21 | src_crontab_SOURCES = \ 22 | src/crontab.c \ 23 | src/security.c \ 24 | $(common_src) 25 | 26 | src_cronnext_SOURCES = \ 27 | src/cronnext.c \ 28 | src/database.c \ 29 | src/job.c \ 30 | src/user.c \ 31 | $(common_src) 32 | 33 | common_src = \ 34 | src/bitstring.h \ 35 | src/entry.c \ 36 | src/env.c \ 37 | src/externs.h \ 38 | src/funcs.h \ 39 | src/globals.h \ 40 | src/macros.h \ 41 | src/misc.c \ 42 | src/pathnames.h \ 43 | src/pw_dup.c \ 44 | src/structs.h 45 | 46 | common_nodist += cron-paths.h 47 | nodist_src_crond_SOURCES = $(common_nodist) 48 | nodist_src_crontab_SOURCES = $(common_nodist) 49 | nodist_src_cronnext_SOURCES = $(common_nodist) 50 | BUILT_SOURCES += $(common_nodist) 51 | 52 | src_crond_LDADD = $(LIBSELINUX) $(LIBPAM) $(LIBAUDIT) 53 | src_crontab_LDADD = $(LIBSELINUX) $(LIBPAM) $(LIBAUDIT) 54 | 55 | ## if DEBUG 56 | ## noinst_PROGRAMS = debug 57 | ## endif 58 | 59 | # This header contains all the paths. 60 | # If they are configurable, they are declared in configure script. 61 | # Depends on this Makefile, because it uses make variables. 62 | # CCD 2010/09/10 added CRON_HOSTNAME for clustered-cron. 63 | CLEANFILES += cron-paths.h 64 | if HAS_RUNSTATE 65 | cronpidcomment=/* directory of cron pid file */ 66 | cronpiddir=\#define CRON_PID_DIR "$(runstatedir)" 67 | else 68 | cronpidcomment= 69 | cronpiddir= 70 | endif 71 | cron-paths.h: Makefile 72 | @echo 'creating $@' 73 | @sed >$@ 's/ *\\$$//' <<\END #\ 74 | /* This file has been automatically generated. Do not edit. */ \ 75 | \ 76 | #ifndef _CRON_PATHS_H_ \ 77 | #define _CRON_PATHS_H_ \ 78 | \ 79 | /* SPOOLDIR is where the crontabs live. \ 80 | * This directory will have its modtime updated \ 81 | * whenever crontab(1) changes a crontab; this is \ 82 | * the signal for cron(8) to look at each individual \ 83 | * crontab file and reload those whose modtimes are \ 84 | * newer than they were last time around (or which \ 85 | * didn't exist last time around...) \ 86 | * or it will be checked by inotify \ 87 | */ \ 88 | #define SPOOL_DIR "$(SPOOL_DIR)" \ 89 | \ 90 | /* CRON_HOSTNAME is file in SPOOL_DIR which, if it \ 91 | * exists, and does not just contain a line matching \ 92 | * the name returned by gethostname(), causes all \ 93 | * crontabs in SPOOL_DIR to be ignored. This is \ 94 | * intended to be used when clustering hosts sharing \ 95 | * one NFS-mounted SPOOL_DIR, and where only one host \ 96 | * should use the crontab files here at any one time. \ 97 | */ \ 98 | #define CRON_HOSTNAME ".cron.hostname" \ 99 | \ 100 | /* cron allow/deny file. At least cron.deny must \ 101 | * exist for ordinary users to run crontab. \ 102 | */ \ 103 | #define CRON_ALLOW "$(sysconfdir)/cron.allow" \ 104 | #define CRON_DENY "$(sysconfdir)/cron.deny" \ 105 | \ 106 | $(cronpidcomment) \ 107 | $(cronpiddir) \ 108 | \ 109 | /* 4.3BSD-style crontab f.e. /etc/crontab */ \ 110 | #define SYSCRONTAB "$(SYSCRONTAB)" \ 111 | \ 112 | /* system crontab dir f.e. /etc/cron.d/ */ \ 113 | #define SYS_CROND_DIR "$(SYS_CROND_DIR)" \ 114 | \ 115 | #define SYSCONFDIR "$(sysconfdir)" \ 116 | \ 117 | #endif /* _CRON_PATHS_H_ */ \ 118 | END 119 | -------------------------------------------------------------------------------- /src/bitstring.h: -------------------------------------------------------------------------------- 1 | /* $NetBSD: bitstring.h,v 1.3 2003/08/07 11:17:08 agc Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1989, 1993 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * This code is derived from software contributed to Berkeley by 8 | * Paul Vixie. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 3. Neither the name of the University nor the names of its contributors 19 | * may be used to endorse or promote products derived from this software 20 | * without specific prior written permission. 21 | * 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 | * SUCH DAMAGE. 33 | * 34 | * @(#)bitstring.h 8.1 (Berkeley) 7/19/93 35 | */ 36 | 37 | #ifndef _BITSTRING_H_ 38 | #define _BITSTRING_H_ 39 | 40 | typedef unsigned char bitstr_t; 41 | 42 | /* internal macros */ 43 | /* byte of the bitstring bit is in */ 44 | #define _bit_byte(bit) \ 45 | ((bit) >> 3) 46 | 47 | /* mask for the bit within its byte */ 48 | #define _bit_mask(bit) \ 49 | (1 << ((bit)&0x7)) 50 | 51 | /* external macros */ 52 | /* bytes in a bitstring of nbits bits */ 53 | #define bitstr_size(nbits) \ 54 | ((((nbits) - 1) >> 3) + 1) 55 | 56 | /* allocate a bitstring */ 57 | #define bit_alloc(nbits) \ 58 | (bitstr_t *)calloc(1, \ 59 | (unsigned int)bitstr_size(nbits) * sizeof(bitstr_t)) 60 | 61 | /* allocate a bitstring on the stack */ 62 | #define bit_decl(name, nbits) \ 63 | (name)[bitstr_size(nbits)] 64 | 65 | /* is bit N of bitstring name set? */ 66 | #define bit_test(name, bit) \ 67 | ((name)[_bit_byte(bit)] & _bit_mask(bit)) 68 | 69 | /* set bit N of bitstring name */ 70 | #define bit_set(name, bit) \ 71 | (name)[_bit_byte(bit)] |= (bitstr_t)_bit_mask(bit) 72 | 73 | /* clear bit N of bitstring name */ 74 | #define bit_clear(name, bit) \ 75 | (name)[_bit_byte(bit)] &= (bitstr_t)~_bit_mask(bit) 76 | 77 | /* clear bits start ... stop in bitstring */ 78 | #define bit_nclear(name, start, stop) { \ 79 | register bitstr_t *_name = name; \ 80 | register int _start = start, _stop = stop; \ 81 | register int _startbyte = _bit_byte(_start); \ 82 | register int _stopbyte = _bit_byte(_stop); \ 83 | if (_startbyte == _stopbyte) { \ 84 | _name[_startbyte] &= (bitstr_t)((0xff >> (8 - (_start&0x7))) | \ 85 | (0xff << ((_stop&0x7) + 1))); \ 86 | } else { \ 87 | _name[_startbyte] &= (bitstr_t)(0xff >> (8 - (_start&0x7))); \ 88 | while (++_startbyte < _stopbyte) \ 89 | _name[_startbyte] = 0; \ 90 | _name[_stopbyte] &= (bitstr_t)(0xff << ((_stop&0x7) + 1)); \ 91 | } \ 92 | } 93 | 94 | /* set bits start ... stop in bitstring */ 95 | #define bit_nset(name, start, stop) { \ 96 | register bitstr_t *_name = name; \ 97 | register int _start = start, _stop = stop; \ 98 | register int _startbyte = _bit_byte(_start); \ 99 | register int _stopbyte = _bit_byte(_stop); \ 100 | if (_startbyte == _stopbyte) { \ 101 | _name[_startbyte] |= (bitstr_t)((0xff << (_start&0x7)) & \ 102 | (0xff >> (7 - (_stop&0x7)))); \ 103 | } else { \ 104 | _name[_startbyte] |= (bitstr_t)(0xff << ((_start)&0x7)); \ 105 | while (++_startbyte < _stopbyte) \ 106 | _name[_startbyte] = 0xff; \ 107 | _name[_stopbyte] |= (bitstr_t)(0xff >> (7 - (_stop&0x7))); \ 108 | } \ 109 | } 110 | 111 | /* find first bit clear in name */ 112 | #define bit_ffc(name, nbits, value) { \ 113 | register bitstr_t *_name = name; \ 114 | register int _byte, _nbits = nbits; \ 115 | register int _stopbyte = _bit_byte(_nbits), _value = -1; \ 116 | for (_byte = 0; _byte <= _stopbyte; ++_byte) \ 117 | if (_name[_byte] != 0xff) { \ 118 | _value = _byte << 3; \ 119 | for (_stopbyte = _name[_byte]; (_stopbyte&0x1); \ 120 | ++_value, _stopbyte >>= 1); \ 121 | break; \ 122 | } \ 123 | *(value) = _value; \ 124 | } 125 | 126 | /* find first bit set in name */ 127 | #define bit_ffs(name, nbits, value) { \ 128 | register bitstr_t *_name = name; \ 129 | register int _byte, _nbits = nbits; \ 130 | register int _stopbyte = _bit_byte(_nbits), _value = -1; \ 131 | for (_byte = 0; _byte <= _stopbyte; ++_byte) \ 132 | if (_name[_byte]) { \ 133 | _value = _byte << 3; \ 134 | for (_stopbyte = _name[_byte]; !(_stopbyte&0x1); \ 135 | ++_value, _stopbyte >>= 1); \ 136 | break; \ 137 | } \ 138 | *(value) = _value; \ 139 | } 140 | 141 | #endif /* !_BITSTRING_H_ */ 142 | -------------------------------------------------------------------------------- /src/cronnext.c: -------------------------------------------------------------------------------- 1 | /* 2 | cronnext - calculate the time cron will execute the next job 3 | Copyright (C) 2016 Marco Migliori 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 2 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License along 16 | with this program; if not, write to the Free Software Foundation, Inc., 17 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | 19 | The GNU General Public License can also be found in the file 20 | `COPYING' that comes with the Anacron source distribution. 21 | */ 22 | 23 | #include "config.h" 24 | 25 | #define MAIN_PROGRAM 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "globals.h" 34 | #include "funcs.h" 35 | #include "cron-paths.h" 36 | 37 | /* flags to crontab search */ 38 | #define ENTRIES 0x01 // print entries 39 | #define CRONTABS 0x02 // print crontabs 40 | #define SYSTEM 0x04 // include system crontab 41 | #define ALLJOBS 0x08 // print all jobs in interval 42 | 43 | #ifdef WITH_INOTIFY 44 | void set_cron_watched(int fd) { 45 | /* empty stub */ 46 | (void)fd; 47 | } 48 | #endif 49 | 50 | void do_command(entry *e, user *u) { 51 | /* empty stub */ 52 | (void)e; 53 | (void)u; 54 | } 55 | 56 | #ifdef WITH_SELINUX 57 | int get_security_context(const char *name, int crontab_fd, 58 | security_context_t *rcontext, const char *tabname) { 59 | /* empty stub */ 60 | (void)name; 61 | (void)crontab_fd; 62 | (void)tabname; 63 | *rcontext = NULL; 64 | return 0; 65 | } 66 | 67 | void free_security_context(security_context_t *scontext) { 68 | /* empty stub */ 69 | (void)scontext; 70 | } 71 | #endif 72 | 73 | /* 74 | * print entry flags 75 | */ 76 | const char *flagname[]= { 77 | "MIN_STAR", 78 | "HR_STAR", 79 | "DOM_STAR", 80 | "DOW_STAR", 81 | "WHEN_REBOOT", 82 | "DONT_LOG" 83 | }; 84 | 85 | void printflags(char *indent, int flags) { 86 | size_t f; 87 | int first = 1; 88 | 89 | printf("%s flagnames:", indent); 90 | for (f = 0; f < sizeof(flagname)/sizeof(char *); f++) 91 | if (flags & (int)1 << f) { 92 | printf("%s%s", first ? " " : "|", flagname[f]); 93 | first = 0; 94 | } 95 | printf("\n"); 96 | } 97 | 98 | /* 99 | * print a crontab entry 100 | */ 101 | void printentry(char *indent, entry *e, time_t next) { 102 | printf("%s - user: %s\n", indent, e->pwd->pw_name); 103 | printf("%s cmd: \"%s\"\n", indent, e->cmd); 104 | printf("%s flags: 0x%02X\n", indent, e->flags); 105 | printflags(indent, e->flags); 106 | printf("%s delay: %d\n", indent, e->delay); 107 | printf("%s next: %ld\n", indent, (long)next); 108 | printf("%s nextstring: ", indent); 109 | printf("%s", asctime(localtime(&next))); 110 | } 111 | 112 | /* 113 | * print a crontab data 114 | */ 115 | void printcrontab(user *u) { 116 | printf(" - user: \"%s\"\n", u->name); 117 | printf(" crontab: %s\n", u->tabname); 118 | printf(" system: %d\n", u->system); 119 | printf(" entries:\n"); 120 | } 121 | 122 | /* 123 | * basic algorithm: iterate over time from now to 8 year ahead in default steps 124 | * of 1 minute, checking whether time matches a crontab entry at each step (8 125 | * years is the largest interval between two crontab matches) 126 | * 127 | * to save iterations, use larger steps if month or day don't match the entry: 128 | * - if the month doesn't match, skip to 00:00 of the first day of next month 129 | * - for the day, avoid the complication of the different length of months: if 130 | * neither the day nor the next day match, increase time of one day 131 | */ 132 | 133 | /* 134 | * check whether time matches day of month and/or day of week; this requires 135 | * checking dom if dow=*, dow if dom=*, either one otherwise; see comment "the 136 | * dom/dow situation is odd..." in cron.c 137 | */ 138 | int matchday(entry *e, time_t time) { 139 | struct tm current; 140 | 141 | localtime_r(&time, ¤t); 142 | 143 | if (e->flags & DOW_STAR) 144 | return bit_test(e->dom, current.tm_mday - 1); 145 | if (e->flags & DOM_STAR) 146 | return bit_test(e->dow, current.tm_wday); 147 | return bit_test(e->dom, current.tm_mday - 1) || 148 | bit_test(e->dow, current.tm_wday); 149 | } 150 | 151 | /* 152 | * next time matching a crontab entry 153 | */ 154 | time_t nextmatch(entry *e, time_t start, time_t end) { 155 | time_t time; 156 | struct tm current; 157 | 158 | for (time = start; time <= end; ) { 159 | localtime_r(&time, ¤t); 160 | 161 | /* month doesn't match: move to 1st of next month */ 162 | if (!bit_test(e->month, current.tm_mon)) { 163 | current.tm_mon++; 164 | if (current.tm_mon >= 12) { 165 | current.tm_year++; 166 | current.tm_mon = 0; 167 | } 168 | current.tm_mday = 1; 169 | current.tm_hour = 0; 170 | current.tm_min = 0; 171 | time = mktime(¤t); 172 | continue; 173 | } 174 | 175 | /* neither time nor time+1day match day: increase 1 day */ 176 | if (!matchday(e, time) && !matchday(e, time + 24 * 60 * 60)) { 177 | time += 24 * 60 * 60; 178 | continue; 179 | } 180 | 181 | /* if time matches, return time; 182 | * check for month is redundant, but check for day is 183 | * necessary because we only know that either time 184 | * or time+1day match */ 185 | if (bit_test(e->month, current.tm_mon) && 186 | matchday(e, time) && 187 | bit_test(e->hour, current.tm_hour) && 188 | bit_test(e->minute, current.tm_min) 189 | ) 190 | return time; 191 | 192 | /* skip to next minute */ 193 | time += 60; 194 | } 195 | 196 | return -1; 197 | } 198 | 199 | /* 200 | * match a user against a list 201 | */ 202 | int matchuser(char *user_name, char *list) { 203 | char *pos; 204 | size_t l = strlen(user_name); 205 | 206 | for (pos = list; (pos = strstr(pos, user_name)) != NULL; pos += l) { 207 | if ((pos != list) && (*(pos - 1) != ',')) 208 | continue; 209 | if ((pos[l] != '\0') && (pos[l] != ',')) 210 | continue; 211 | return 1; 212 | } 213 | return 0; 214 | } 215 | 216 | /* 217 | * find the next scheduled job 218 | */ 219 | time_t cronnext(cron_db database, 220 | time_t start, time_t end, 221 | char *include, char *exclude, char *command, int flags) { 222 | time_t closest, next; 223 | user *u; 224 | entry *e; 225 | char *indent = ""; 226 | 227 | if (flags & CRONTABS) { 228 | printf("crontabs:\n"); 229 | indent = " "; 230 | } 231 | else if (flags & ALLJOBS) 232 | printf("jobs:\n"); 233 | 234 | /* find the next scheduled time */ 235 | closest = -1; 236 | for (u = database.head; u; u = u->next) { 237 | if (include && !matchuser(u->name, include)) 238 | continue; 239 | if (exclude && matchuser(u->name, exclude)) 240 | continue; 241 | if (!(flags & SYSTEM) && u->system) 242 | continue; 243 | 244 | if (flags & CRONTABS) 245 | printcrontab(u); 246 | 247 | for (e = u->crontab; e; e = e->next) { 248 | if (command && strstr(e->cmd, command) == NULL) 249 | continue; 250 | for (next = nextmatch(e, start, end); 251 | next <= end; 252 | next = nextmatch(e, next + 60, end)) { 253 | if (next < 0) 254 | break; 255 | if (closest < 0 || next < closest) 256 | closest = next; 257 | if (flags & ENTRIES) 258 | printentry(indent, e, next); 259 | if (! (flags & ALLJOBS)) 260 | break; 261 | } 262 | } 263 | } 264 | 265 | return closest; 266 | } 267 | 268 | /* 269 | * load installed crontabs and/or crontab files 270 | */ 271 | cron_db database(int installed, char **additional) { 272 | cron_db db = {NULL, NULL, (time_t) 0}; 273 | struct passwd pw; 274 | int fd; 275 | struct stat ss; 276 | user *u; 277 | 278 | if (installed) 279 | load_database(&db); 280 | 281 | for ( ; *additional != NULL; additional++) { 282 | fd = open(*additional, O_RDONLY); 283 | if (fd == -1) { 284 | perror(*additional); 285 | continue; 286 | } 287 | fstat(fd, &ss); 288 | if (S_ISDIR(ss.st_mode)) { 289 | fprintf(stderr, "%s is a directory - skipping\n", *additional); 290 | close(fd); 291 | continue; 292 | } 293 | memset(&pw, 0, sizeof(pw)); 294 | pw.pw_name = *additional; 295 | pw.pw_passwd = ""; 296 | pw.pw_dir = "."; 297 | u = load_user(fd, &pw, *additional, *additional, *additional); 298 | if (u == NULL) { 299 | printf("cannot load crontab %s\n", *additional); 300 | continue; 301 | } 302 | link_user(&db, u); 303 | } 304 | 305 | return db; 306 | } 307 | 308 | void usage() { 309 | fprintf(stderr, "Find the time of the next scheduled cron job.\n"); 310 | fprintf(stderr, "Usage:\n"); 311 | fprintf(stderr, " cronnext [options] [file ...]\n"); 312 | fprintf(stderr, "\n"); 313 | fprintf(stderr, "Options:\n"); 314 | fprintf(stderr, " -i users include only the crontab of these users\n"); 315 | fprintf(stderr, " -e users exclude the crontab of these users\n"); 316 | fprintf(stderr, " -s do not include the system crontab\n"); 317 | fprintf(stderr, " -a examine installed crontabs even if files are given\n"); 318 | fprintf(stderr, " -t time start from this time (seconds since epoch)\n"); 319 | fprintf(stderr, " -q time end check at this time (seconds since epoch)\n"); 320 | fprintf(stderr, " -j cmd only check jobs that contain cmd as a substring\n"); 321 | fprintf(stderr, " -l print next jobs to be executed\n"); 322 | fprintf(stderr, " -c print next execution of each job\n"); 323 | fprintf(stderr, " -f print all jobs executed in the given interval\n"); 324 | fprintf(stderr, " -h this help\n"); 325 | fprintf(stderr, " -V print version and exit\n"); 326 | } 327 | 328 | /* 329 | * main 330 | */ 331 | int main(int argn, char *argv[]) { 332 | int opt; 333 | char *include, *exclude, *command; 334 | int flags; 335 | time_t start, next, end = 0; 336 | int endtime, printjobs; 337 | cron_db db; 338 | int installed = 0; 339 | 340 | include = NULL; 341 | exclude = NULL; 342 | command = NULL; 343 | flags = SYSTEM; 344 | endtime = 0; 345 | printjobs = 0; 346 | start = (time(NULL) + 59) / 60 * 60; 347 | 348 | while (-1 != (opt = getopt(argn, argv, "i:e:ast:q:j:lcfhV"))) { 349 | switch (opt) { 350 | case 'i': 351 | include = optarg; 352 | break; 353 | case 'e': 354 | exclude = optarg; 355 | break; 356 | case 'a': 357 | installed = 1; 358 | break; 359 | case 's': 360 | flags &= ~SYSTEM; 361 | break; 362 | case 't': 363 | start = (atoi(optarg) + 59) / 60 * 60; 364 | break; 365 | case 'q': 366 | end = atoi(optarg) / 60 * 60; 367 | endtime = 1; 368 | break; 369 | case 'j': 370 | command = optarg; 371 | break; 372 | case 'l': 373 | printjobs = 1; 374 | break; 375 | case 'c': 376 | flags |= ENTRIES | CRONTABS; 377 | break; 378 | case 'f': 379 | flags |= ALLJOBS | ENTRIES; 380 | break; 381 | case 'h': 382 | usage(); 383 | return EXIT_SUCCESS; 384 | case 'V': 385 | puts(PACKAGE_STRING); 386 | return EXIT_SUCCESS; 387 | default: 388 | fprintf(stderr, "unrecognized option: %s\n", 389 | argv[optind - 1]); 390 | usage(); 391 | exit(EXIT_FAILURE); 392 | } 393 | } 394 | 395 | if (flags & ALLJOBS && !endtime) { 396 | fprintf(stderr, "no ending time specified: -f requires -q\n"); 397 | usage(); 398 | exit(EXIT_FAILURE); 399 | } 400 | 401 | /* maximum match interval is 8 years: 402 | * crontab has '* * 29 2 *' and we are on 1 March 2096: 403 | * next matching time will be 29 February 2104 */ 404 | if (!endtime) 405 | end = start + 8 * 12 * 31 * 24 * 60 * 60; 406 | 407 | /* debug cron */ 408 | if (flags & CRONTABS) { 409 | printf("spool: %s\n", SPOOL_DIR); 410 | set_debug_flags(""); 411 | } 412 | /* "load,pars" for debugging loading and parsing, "" for nothing 413 | see globals.h for symbolic names and macros.h for meaning */ 414 | 415 | /* load database */ 416 | db = database(installed || argv[optind] == NULL, argv + optind); 417 | 418 | /* find time of next scheduled command */ 419 | next = cronnext(db, start, end, include, exclude, command, flags); 420 | 421 | /* print time */ 422 | if (next == -1) 423 | return EXIT_FAILURE; 424 | else 425 | printf("next: %ld\n", (long) next); 426 | 427 | /* print next jobs */ 428 | if (printjobs) { 429 | printf("nextjobs:\n"); 430 | cronnext(db, next, next, include, exclude, command, (flags & SYSTEM) | ENTRIES); 431 | } 432 | 433 | return EXIT_SUCCESS; 434 | } 435 | 436 | -------------------------------------------------------------------------------- /src/env.c: -------------------------------------------------------------------------------- 1 | /* Copyright 1988,1990,1993,1994 by Paul Vixie 2 | * All rights reserved 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 7 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | #include "config.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "globals.h" 33 | #include "funcs.h" 34 | 35 | #if defined(BSD) 36 | extern char **environ; 37 | #endif 38 | 39 | char **env_init(void) { 40 | char **p = (char **) malloc(sizeof (char *)); 41 | 42 | if (p != NULL) 43 | p[0] = NULL; 44 | return (p); 45 | } 46 | 47 | void env_free(char **envp) { 48 | char **p; 49 | 50 | for (p = envp; *p != NULL; p++) 51 | free(*p); 52 | free(envp); 53 | } 54 | 55 | char **env_copy(char **envp) { 56 | int save_errno; 57 | size_t count, i; 58 | char **p; 59 | 60 | for (count = 0; envp[count] != NULL; count++) ; 61 | 62 | p = (char **) malloc((count + 1) * sizeof (char *)); /* 1 for the NULL */ 63 | if (p != NULL) { 64 | for (i = 0; i < count; i++) 65 | if ((p[i] = strdup(envp[i])) == NULL) { 66 | save_errno = errno; 67 | while (i-- > 0) 68 | free(p[i]); 69 | free(p); 70 | errno = save_errno; 71 | return (NULL); 72 | } 73 | p[count] = NULL; 74 | } 75 | return (p); 76 | } 77 | 78 | char **env_set(char **envp, const char *envstr) { 79 | size_t count, found; 80 | char **p, *envtmp; 81 | 82 | /* 83 | * count the number of elements, including the null pointer; 84 | * also set 'found' to -1 or index of entry if already in here. 85 | */ 86 | found = (size_t)-1; 87 | for (count = 0; envp[count] != NULL; count++) { 88 | if (!strcmp_until(envp[count], envstr, '=')) 89 | found = count; 90 | } 91 | count++; /* for the NULL */ 92 | 93 | if (found != (size_t)-1) { 94 | /* 95 | * it exists already, so just free the existing setting, 96 | * save our new one there, and return the existing array. 97 | */ 98 | if ((envtmp = strdup(envstr)) == NULL) 99 | return (NULL); 100 | free(envp[found]); 101 | envp[found] = envtmp; 102 | return (envp); 103 | } 104 | 105 | /* 106 | * it doesn't exist yet, so resize the array, move null pointer over 107 | * one, save our string over the old null pointer, and return resized 108 | * array. 109 | */ 110 | if ((envtmp = strdup(envstr)) == NULL) 111 | return (NULL); 112 | p = (char **) realloc((void *) envp, 113 | (count + 1) * sizeof (char *)); 114 | if (p == NULL) { 115 | free(envtmp); 116 | return (NULL); 117 | } 118 | p[count] = p[count - 1]; 119 | p[count - 1] = envtmp; 120 | return (p); 121 | } 122 | 123 | int env_set_from_environ(char ***envpp) { 124 | static const char *names[] = { 125 | "LANG", 126 | "LC_CTYPE", 127 | "LC_NUMERIC", 128 | "LC_TIME", 129 | "LC_COLLATE", 130 | "LC_MONETARY", 131 | "LC_MESSAGES", 132 | "LC_PAPER", 133 | "LC_NAME", 134 | "LC_ADDRESS", 135 | "LC_TELEPHONE", 136 | "LC_MEASUREMENT", 137 | "LC_IDENTIFICATION", 138 | "LC_ALL", 139 | "LANGUAGE", 140 | "RANDOM_DELAY", 141 | "MAILFROM", 142 | NULL 143 | }; 144 | const char **name; 145 | char **procenv; 146 | 147 | for (procenv = environ; *procenv != NULL; ++procenv) { 148 | for (name = names; *name != NULL; ++name) { 149 | size_t namelen; 150 | 151 | namelen = strlen(*name); 152 | if (strncmp(*name, *procenv, namelen) == 0 153 | && (*procenv)[namelen] == '=') { 154 | char **tmpenv; 155 | 156 | tmpenv = env_set(*envpp, *procenv); 157 | if (tmpenv == NULL) 158 | return FALSE; 159 | *envpp = tmpenv; 160 | } 161 | } 162 | } 163 | return TRUE; 164 | } 165 | 166 | /* The following states are used by load_env(), traversed in order: */ 167 | enum env_state { 168 | NAMEI, /* First char of NAME, may be quote */ 169 | NAME, /* Subsequent chars of NAME */ 170 | EQ1, /* After end of name, looking for '=' sign */ 171 | EQ2, /* After '=', skipping whitespace */ 172 | VALUEI, /* First char of VALUE, may be quote */ 173 | VALUE, /* Subsequent chars of VALUE */ 174 | FINI, /* All done, skipping trailing whitespace */ 175 | ERROR, /* Error */ 176 | }; 177 | 178 | /* return ERR = end of file 179 | * FALSE = not an env setting (file was repositioned) 180 | * TRUE = was an env setting 181 | */ 182 | int load_env(char *envstr, FILE * f) { 183 | long filepos; 184 | int fileline; 185 | enum env_state state; 186 | char quotechar, *c, *str, *val; 187 | 188 | filepos = ftell(f); 189 | fileline = LineNumber; 190 | if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n")) 191 | return (ERR); 192 | 193 | Debug(DPARS, ("load_env, read <%s>\n", envstr)); 194 | 195 | val = str = envstr; 196 | state = NAMEI; 197 | quotechar = '\0'; 198 | c = envstr; 199 | while (state != ERROR && *c) { 200 | switch (state) { 201 | case NAMEI: 202 | case VALUEI: 203 | if (*c == '\'' || *c == '"') 204 | quotechar = *c++; 205 | state++; 206 | /* FALLTHROUGH */ 207 | case NAME: 208 | case VALUE: 209 | if (quotechar) { 210 | if (*c == quotechar) { 211 | state++; 212 | c++; 213 | break; 214 | } 215 | if (state == NAME && *c == '=') { 216 | state = ERROR; 217 | break; 218 | } 219 | } 220 | else { 221 | if (state == NAME) { 222 | if (isspace((unsigned char) *c)) { 223 | c++; 224 | state++; 225 | break; 226 | } 227 | if (*c == '=') { 228 | state++; 229 | break; 230 | } 231 | } 232 | } 233 | *str++ = *c++; 234 | break; 235 | 236 | case EQ1: 237 | if (*c == '=') { 238 | state++; 239 | quotechar = '\0'; 240 | *str++ = *c; 241 | val = str; 242 | } 243 | else { 244 | if (!isspace((unsigned char) *c)) 245 | state = ERROR; 246 | } 247 | c++; 248 | break; 249 | 250 | case EQ2: 251 | case FINI: 252 | if (isspace((unsigned char) *c)) 253 | c++; 254 | else 255 | state++; 256 | break; 257 | 258 | default: 259 | abort(); 260 | } 261 | } 262 | if (state != FINI && state != EQ2 && !(state == VALUE && !quotechar)) { 263 | Debug(DPARS, ("load_env, not an env var, state = %d\n", state)); 264 | if (fseek(f, filepos, SEEK_SET)) { 265 | return ERR; 266 | } 267 | Set_LineNum(fileline); 268 | return (FALSE); 269 | } 270 | *str = '\0'; 271 | if (state == VALUE) { 272 | /* End of unquoted value: trim trailing whitespace */ 273 | while (str > val && isspace((unsigned char)str[-1])) 274 | *(--str) = '\0'; 275 | } 276 | return TRUE; 277 | } 278 | 279 | char *env_get(const char *name, char **envp) { 280 | size_t len = strlen(name); 281 | char *p, *q; 282 | 283 | while ((p = *envp++) != NULL) { 284 | if (!(q = strchr(p, '='))) 285 | continue; 286 | if ((size_t)(q - p) == len && !strncmp(p, name, len)) 287 | return (q + 1); 288 | } 289 | return (NULL); 290 | } 291 | 292 | char **env_update_home(char **envp, const char *dir) { 293 | char envstr[MAX_ENVSTR]; 294 | 295 | if (dir == NULL || *dir == '\0' || env_get("HOME", envp)) { 296 | return envp; 297 | } 298 | 299 | if (glue_strings(envstr, sizeof envstr, "HOME", dir, '=')) { 300 | envp = env_set(envp, envstr); 301 | } 302 | else 303 | log_it("CRON", getpid(), "ERROR", "can't set HOME", 0); 304 | 305 | return envp; 306 | } 307 | -------------------------------------------------------------------------------- /src/externs.h: -------------------------------------------------------------------------------- 1 | /* Copyright 1993,1994 by Paul Vixie 2 | * All rights reserved 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 7 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | /* reorder these #include's at your peril */ 23 | 24 | #ifndef CRONIE_EXTERNS_H 25 | #define CRONIE_EXTERNS_H 26 | 27 | #if defined(LOGIN_CAP) 28 | # include 29 | #endif /*LOGIN_CAP*/ 30 | 31 | #if defined(BSD_AUTH) 32 | # include 33 | #endif /*BSD_AUTH*/ 34 | 35 | #define DIR_T struct dirent 36 | #define WAIT_T int 37 | #define SIG_T sig_t 38 | #define TIME_T time_t 39 | #define PID_T pid_t 40 | 41 | #ifndef TZNAME_ALREADY_DEFINED 42 | extern char *tzname[2]; 43 | #endif 44 | #define TZONE(tm) tzname[(tm).tm_isdst] 45 | 46 | #if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(__sun) || defined(_AIX) 47 | # define HAVE_SAVED_UIDS 48 | #endif 49 | 50 | #define MY_UID(pw) getuid() 51 | #define MY_GID(pw) getgid() 52 | 53 | /* getopt() isn't part of POSIX. some systems define it in anyway. 54 | * of those that do, some complain that our definition is different and some 55 | * do not. to add to the misery and confusion, some systems define getopt() 56 | * in ways that we cannot predict or comprehend, yet do not define the adjunct 57 | * external variables needed for the interface. 58 | */ 59 | #if (!defined(BSD) || (BSD < 198911)) 60 | int getopt(int, char * const *, const char *); 61 | #endif 62 | 63 | #if (!defined(BSD) || (BSD < 199103)) 64 | extern char *optarg; 65 | extern int optind, opterr, optopt; 66 | #endif 67 | 68 | /* digital unix needs this but does not give us a way to identify it. 69 | */ 70 | extern int flock(int, int); 71 | 72 | /* not all systems who provide flock() provide these definitions. 73 | */ 74 | #ifndef LOCK_SH 75 | # define LOCK_SH 1 76 | #endif 77 | #ifndef LOCK_EX 78 | # define LOCK_EX 2 79 | #endif 80 | #ifndef LOCK_NB 81 | # define LOCK_NB 4 82 | #endif 83 | #ifndef LOCK_UN 84 | # define LOCK_UN 8 85 | #endif 86 | 87 | #ifndef WCOREDUMP 88 | # define WCOREDUMP(st) (((st) & 0200) != 0) 89 | #endif 90 | 91 | #endif /* CRONIE_EXTERNS_H */ 92 | -------------------------------------------------------------------------------- /src/funcs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: funcs.h,v 1.9 2004/01/23 18:56:42 vixie Exp $ 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 7 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | /* Notes: 23 | * We should reorg this into sections by module. 24 | */ 25 | 26 | #ifndef CRONIE_FUNCS_H 27 | #define CRONIE_FUNCS_H 28 | 29 | #include 30 | #include 31 | 32 | #ifdef WITH_SELINUX 33 | #include 34 | #endif 35 | 36 | #include "externs.h" 37 | #include "structs.h" 38 | 39 | void set_cron_uid(void), 40 | check_spool_dir(void), 41 | open_logfile(void), 42 | sigpipe_func(void), 43 | job_add(entry *, user *), 44 | do_command(entry *, user *), 45 | link_user(cron_db *, user *), 46 | unlink_user(cron_db *, user *), 47 | free_user(user *), 48 | env_free(char **), 49 | unget_char(int, FILE *), 50 | free_entry(entry *), 51 | acquire_daemonlock(int), 52 | log_it(const char *, PID_T, const char *, const char *, int), 53 | log_close(void), 54 | check_orphans(cron_db *); 55 | #if defined WITH_INOTIFY 56 | void set_cron_watched(int ), 57 | set_cron_unwatched(int ), 58 | check_inotify_database(cron_db *); 59 | #endif 60 | 61 | int load_database(cron_db *), 62 | job_runqueue(void), 63 | set_debug_flags(const char *), 64 | get_char(FILE *), 65 | get_string(char *, int, FILE *, const char *), 66 | swap_uids(void), 67 | swap_uids_back(void), 68 | load_env(char *, FILE *), 69 | env_set_from_environ(char ***envpp), 70 | cron_pabort(FILE *), 71 | cron_pclose(FILE *), 72 | glue_strings(char *, size_t, const char *, const char *, char), 73 | strcmp_until(const char *, const char *, char), 74 | skip_comments(FILE *), 75 | allowed(const char * ,const char * ,const char *); 76 | 77 | size_t strlens(const char *, ...), 78 | strdtb(char *); 79 | 80 | char *env_get(const char *, char **), 81 | *arpadate(time_t *), 82 | *mkprints(unsigned char *, size_t), 83 | *first_word(const char *, const char *), 84 | **env_init(void), 85 | **env_copy(char **), 86 | **env_set(char **, const char *), 87 | **env_update_home(char **, const char *); 88 | 89 | user *load_user(int, struct passwd *, const char *, const char *, const char *), 90 | *find_user(cron_db *, const char *, const char *); 91 | 92 | entry *load_entry(FILE *, void (*)(const char *), struct passwd *, char **); 93 | 94 | FILE *cron_popen(char *, const char *, struct passwd *, char **); 95 | 96 | struct passwd *pw_dup(const struct passwd *); 97 | 98 | #ifndef HAVE_STRUCT_TM_TM_GMTOFF 99 | long get_gmtoff(time_t *, struct tm *); 100 | #endif 101 | 102 | /* Red Hat security stuff (security.c): 103 | */ 104 | void cron_restore_default_security_context( void ); 105 | 106 | int cron_set_job_security_context( entry *e, user *u, char ***jobenvp ); 107 | 108 | int cron_open_security_session( struct passwd *pw ); 109 | 110 | void cron_close_security_session( void ); 111 | 112 | int cron_change_groups( struct passwd *pw ); 113 | 114 | int cron_change_user_permanently( struct passwd *pw, char *homedir ); 115 | 116 | int get_security_context(const char *name, 117 | int crontab_fd, 118 | security_context_t *rcontext, 119 | const char *tabname 120 | ); 121 | 122 | void free_security_context( security_context_t *scontext ); 123 | 124 | int crontab_security_access(void); 125 | 126 | /* PAM */ 127 | #ifdef WITH_PAM 128 | int cron_start_pam(struct passwd *pw); 129 | void cron_close_pam(void); 130 | #endif 131 | 132 | #endif /* CRONIE_FUNCS_H */ 133 | -------------------------------------------------------------------------------- /src/globals.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: globals.h,v 1.10 2004/01/23 19:03:33 vixie Exp $ 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 7 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | /* 23 | * Modified 2010/09/12 by Colin Dean, Durham University IT Service, 24 | * to add clustering support. 25 | */ 26 | 27 | #ifndef CRONIE_GLOBALS_H 28 | #define CRONIE_GLOBALS_H 29 | 30 | #include 31 | 32 | #include "macros.h" 33 | 34 | #ifdef MAIN_PROGRAM 35 | # define XTRN 36 | # define INIT(x) = x 37 | #else 38 | # define XTRN extern 39 | # define INIT(x) 40 | #endif 41 | 42 | XTRN const char *copyright[] 43 | #ifdef MAIN_PROGRAM 44 | = { 45 | "@(#) ISC Cron V4.1", 46 | "@(#) Copyright 1988,1989,1990,1993,1994 by Paul Vixie", 47 | "@(#) Copyright 1997,2000 by Internet Software Consortium, Inc.", 48 | "@(#) Copyright 2004 by Internet Systems Consortium, Inc.", 49 | "@(#) All rights reserved", 50 | NULL 51 | } 52 | #endif 53 | ; 54 | 55 | XTRN const char *MonthNames[] 56 | #ifdef MAIN_PROGRAM 57 | = { 58 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", 59 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 60 | NULL 61 | } 62 | #endif 63 | ; 64 | 65 | XTRN const char *DowNames[] 66 | #ifdef MAIN_PROGRAM 67 | = { 68 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", 69 | NULL 70 | } 71 | #endif 72 | ; 73 | 74 | XTRN char *ProgramName; 75 | XTRN int LineNumber; 76 | XTRN int SyslogOutput; 77 | XTRN time_t StartTime; 78 | XTRN int NoFork; 79 | XTRN int PermitAnyCrontab; 80 | XTRN char MailCmd[MAX_COMMAND+1]; /* +1 for terminator */ 81 | XTRN char cron_default_mail_charset[MAX_ENVSTR]; 82 | XTRN int EnableClustering; 83 | XTRN int ChangePath; 84 | XTRN double RandomScale; 85 | 86 | #if DEBUGGING 87 | XTRN int DebugFlags INIT(0); 88 | XTRN const char *DebugFlagNames[] 89 | #ifdef MAIN_PROGRAM 90 | = { 91 | "ext", "sch", "proc", "pars", "load", "misc", "test", "bit", 92 | NULL 93 | } 94 | #endif 95 | ; 96 | #else 97 | #define DebugFlags 0 98 | #endif /* DEBUGGING */ 99 | 100 | #endif /* CRONIE_GLOBALS_H */ 101 | -------------------------------------------------------------------------------- /src/job.c: -------------------------------------------------------------------------------- 1 | /* Copyright 1988,1990,1993,1994 by Paul Vixie 2 | * All rights reserved 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 7 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | #include "config.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "funcs.h" 32 | #include "globals.h" 33 | 34 | typedef struct _job { 35 | struct _job *next; 36 | entry *e; 37 | user *u; 38 | } job; 39 | 40 | static job *jhead = NULL, *jtail = NULL; 41 | 42 | void job_add(entry * e, user * u) { 43 | job *j; 44 | struct passwd *newpwd; 45 | struct passwd *temppwd; 46 | const char *uname; 47 | 48 | /* if already on queue, keep going */ 49 | for (j = jhead; j != NULL; j = j->next) 50 | if (j->e == e && j->u == u) 51 | return; 52 | 53 | uname = e->pwd->pw_name; 54 | /* check if user exists in time of job is being run f.e. ldap */ 55 | if ((temppwd = getpwnam(uname)) != NULL) { 56 | char **tenvp; 57 | 58 | Debug(DSCH | DEXT, ("user [%s:%ld:%ld:...] cmd=\"%s\"\n", 59 | e->pwd->pw_name, (long) temppwd->pw_uid, 60 | (long) temppwd->pw_gid, e->cmd)); 61 | if ((newpwd = pw_dup(temppwd)) == NULL) { 62 | log_it(uname, getpid(), "ERROR", "memory allocation failed", errno); 63 | return; 64 | } 65 | free(e->pwd); 66 | e->pwd = newpwd; 67 | 68 | if ((tenvp = env_update_home(e->envp, e->pwd->pw_dir)) == NULL) { 69 | log_it(uname, getpid(), "ERROR", "memory allocation failed", errno); 70 | return; 71 | } 72 | e->envp = tenvp; 73 | } else { 74 | log_it(uname, getpid(), "ERROR", "getpwnam() failed - user unknown",errno); 75 | Debug(DSCH | DEXT, ("%s:%d pid=%d time=%lld getpwnam(%s) failed errno=%d error=%s\n", 76 | __FILE__,__LINE__,getpid(),(long long)time(NULL),uname,errno,strerror(errno))); 77 | return; 78 | } 79 | 80 | /* build a job queue element */ 81 | if ((j = (job *) malloc(sizeof (job))) == NULL) 82 | return; 83 | j->next = NULL; 84 | j->e = e; 85 | j->u = u; 86 | 87 | /* add it to the tail */ 88 | if (jhead == NULL) 89 | jhead = j; 90 | else 91 | jtail->next = j; 92 | jtail = j; 93 | } 94 | 95 | int job_runqueue(void) { 96 | job *j, *jn; 97 | int run = 0; 98 | 99 | for (j = jhead; j; j = jn) { 100 | do_command(j->e, j->u); 101 | jn = j->next; 102 | free(j); 103 | run++; 104 | } 105 | jhead = jtail = NULL; 106 | return (run); 107 | } 108 | -------------------------------------------------------------------------------- /src/macros.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: macros.h,v 1.9 2004/01/23 18:56:43 vixie Exp $ 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 7 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | #ifndef CRONIE_MACROS_H 23 | #define CRONIE_MACROS_H 24 | 25 | #ifdef HAVE_LIMITS_H 26 | #include 27 | #endif 28 | 29 | /* these are really immutable, and are 30 | * defined for symbolic convenience only 31 | * TRUE, FALSE, and ERR must be distinct 32 | * ERR must be < OK. 33 | */ 34 | #define TRUE 1 35 | #define FALSE 0 36 | /* system calls return this on success */ 37 | #define OK 0 38 | /* or this on error */ 39 | #define ERR (-1) 40 | 41 | /* turn this on to get '-x' code */ 42 | #ifndef DEBUGGING 43 | #define DEBUGGING FALSE 44 | #endif 45 | 46 | #define INIT_PID 1 /* parent of orphans */ 47 | #define READ_PIPE 0 /* which end of a pipe pair do you read? */ 48 | #define WRITE_PIPE 1 /* or write to? */ 49 | #define STDIN 0 /* what is stdin's file descriptor? */ 50 | #define STDOUT 1 /* stdout's? */ 51 | #define STDERR 2 /* stderr's? */ 52 | #define ERROR_EXIT 1 /* exit() with this will scare the shell */ 53 | #define OK_EXIT 0 /* exit() with this is considered 'normal' */ 54 | #define MAX_FNAME PATH_MAX/* max length of internally generated fn */ 55 | #define MAX_COMMAND 131072 /* max length of internally generated cmd (max sh cmd line length) */ 56 | #define MAX_ENVSTR 131072 /* max length of envvar=value\0 strings */ 57 | #define MAX_TEMPSTR 131072 /* obvious */ 58 | #define MAX_UNAME 256 /* max length of username */ 59 | #define ROOT_UID 0 /* don't change this, it really must be root */ 60 | #define ROOT_USER "root" /* ditto */ 61 | #define MAX_USER_ENVS 1000 /* maximum environment variables in user's crontab */ 62 | #define MAX_USER_ENTRIES 10000 /* maximum crontab entries in user's crontab */ 63 | #define MAX_GARBAGE 32768 /* max num of chars of comments and whitespaces between entries */ 64 | #define MAX_CLOSE_FD 10000 /* max fd num to close when spawning a child process */ 65 | 66 | /* NOTE: these correspond to DebugFlagNames, 67 | * defined below. 68 | */ 69 | #define DEXT 0x0001 /* extend flag for other debug masks */ 70 | #define DSCH 0x0002 /* scheduling debug mask */ 71 | #define DPROC 0x0004 /* process control debug mask */ 72 | #define DPARS 0x0008 /* parsing debug mask */ 73 | #define DLOAD 0x0010 /* database loading debug mask */ 74 | #define DMISC 0x0020 /* misc debug mask */ 75 | #define DTEST 0x0040 /* test mode: don't execute any commands */ 76 | 77 | #define PPC_NULL ((const char **)NULL) 78 | 79 | #ifndef MAXHOSTNAMELEN 80 | #define MAXHOSTNAMELEN 64 81 | #endif 82 | 83 | #define Skip_Blanks(c, f) \ 84 | while (c == '\t' || c == ' ') \ 85 | c = get_char(f); 86 | 87 | #define Skip_Nonblanks(c, f) \ 88 | while (c!='\t' && c!=' ' && c!='\n' && c != EOF) \ 89 | c = get_char(f); 90 | 91 | #if DEBUGGING 92 | # define Debug(mask, message) \ 93 | if ((DebugFlags & (mask)) != 0) \ 94 | printf message 95 | #else /* !DEBUGGING */ 96 | # define Debug(mask, message) \ 97 | () 98 | #endif /* DEBUGGING */ 99 | 100 | #define MkUpper(ch) (islower(ch) ? toupper(ch) : ch) 101 | #define Set_LineNum(ln) {Debug(DPARS|DEXT,("linenum=%d\n",ln)); \ 102 | LineNumber = ln; \ 103 | } 104 | 105 | #ifdef HAVE_STRUCT_TM_TM_GMTOFF 106 | #define get_gmtoff(c, t) ((t)->tm_gmtoff) 107 | #endif 108 | 109 | #define SECONDS_PER_MINUTE 60 110 | #define SECONDS_PER_HOUR 3600 111 | 112 | #define FIRST_MINUTE 0 113 | #define LAST_MINUTE 59 114 | #define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1) 115 | 116 | #define FIRST_HOUR 0 117 | #define LAST_HOUR 23 118 | #define HOUR_COUNT (LAST_HOUR - FIRST_HOUR + 1) 119 | 120 | #define FIRST_DOM 1 121 | #define LAST_DOM 31 122 | #define DOM_COUNT (LAST_DOM - FIRST_DOM + 1) 123 | 124 | #define FIRST_MONTH 1 125 | #define LAST_MONTH 12 126 | #define MONTH_COUNT (LAST_MONTH - FIRST_MONTH + 1) 127 | 128 | /* note on DOW: 0 and 7 are both Sunday, for compatibility reasons. */ 129 | #define FIRST_DOW 0 130 | #define LAST_DOW 7 131 | #define DOW_COUNT (LAST_DOW - FIRST_DOW + 1) 132 | 133 | #define TMAX(a,b) ((a)>(b)?(a):(b)) 134 | #define TMIN(a,b) ((a)<(b)?(a):(b)) 135 | 136 | /* 137 | * Because crontab/at files may be owned by their respective users we 138 | * take extreme care in opening them. If the OS lacks the O_NOFOLLOW 139 | * we will just have to live without it. In order for this to be an 140 | * issue an attacker would have to subvert group CRON_GROUP. 141 | */ 142 | #include 143 | #ifndef O_NOFOLLOW 144 | #define O_NOFOLLOW 0 145 | #endif 146 | 147 | #endif /* CRONIE_MACROS_H */ 148 | -------------------------------------------------------------------------------- /src/pathnames.h: -------------------------------------------------------------------------------- 1 | /* Copyright 1993,1994 by Paul Vixie 2 | * All rights reserved 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 7 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | /* 23 | * $Id: pathnames.h,v 1.9 2004/01/23 18:56:43 vixie Exp $ 24 | */ 25 | 26 | #ifndef _PATHNAMES_H_ 27 | #define _PATHNAMES_H_ 28 | 29 | #if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX) 30 | # include 31 | #endif /*BSD*/ 32 | 33 | #include "cron-paths.h" 34 | 35 | /* where should the daemon stick its PID? 36 | * PIDDIR must end in '/'. 37 | * (Don't ask why the default is "/etc/".) 38 | */ 39 | #ifdef CRON_PID_DIR 40 | # define PIDDIR CRON_PID_DIR "/" 41 | #else 42 | # ifdef _PATH_VARRUN 43 | # define PIDDIR _PATH_VARRUN 44 | # else 45 | # define PIDDIR SYSCONFDIR "/" 46 | # endif 47 | #endif 48 | #define PIDFILE "crond.pid" 49 | #define _PATH_CRON_PID PIDDIR PIDFILE 50 | #define REBOOT_LOCK PIDDIR "cron.reboot" 51 | 52 | #ifndef _PATH_BSHELL 53 | # define _PATH_BSHELL "/bin/sh" 54 | #endif 55 | 56 | #ifndef _PATH_STDPATH 57 | # define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" 58 | #endif 59 | 60 | #ifndef _PATH_TMP 61 | # define _PATH_TMP "/tmp" 62 | #endif 63 | 64 | #ifndef _PATH_DEVNULL 65 | # define _PATH_DEVNULL "/dev/null" 66 | #endif 67 | 68 | #endif /* _PATHNAMES_H_ */ 69 | -------------------------------------------------------------------------------- /src/popen.c: -------------------------------------------------------------------------------- 1 | /* $NetBSD: popen.c,v 1.9 2005/03/16 02:53:55 xtraeme Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 1988, 1993, 1994 5 | * The Regents of the University of California. All rights reserved. 6 | * 7 | * This code is derived from software written by Ken Arnold and 8 | * published in UNIX Review, Vol. 6, No. 8. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer. 15 | * 2. Redistributions in binary form must reproduce the above copyright 16 | * notice, this list of conditions and the following disclaimer in the 17 | * documentation and/or other materials provided with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 | * SUCH DAMAGE. 30 | * 31 | */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | #include "funcs.h" 44 | #include "globals.h" 45 | #include "macros.h" 46 | 47 | #ifdef HAVE_SYS_CDEFS_H 48 | # include 49 | #endif 50 | 51 | #include 52 | 53 | /* 54 | * Special version of popen which avoids call to shell. This insures no one 55 | * may create a pipe to a hidden program as a side effect of a list or dir 56 | * command. 57 | */ 58 | static PID_T *pids; 59 | static int fds; 60 | 61 | #define MAX_ARGS 1024 62 | 63 | FILE *cron_popen(char *program, const char *type, struct passwd *pw, char **jobenv) { 64 | char *cp; 65 | FILE *iop; 66 | int argc, pdes[2]; 67 | PID_T pid; 68 | char *argv[MAX_ARGS]; 69 | ssize_t out; 70 | char buf[PIPE_BUF]; 71 | struct sigaction sa; 72 | int fd; 73 | 74 | #ifdef __GNUC__ 75 | (void) &iop; /* Avoid fork clobbering */ 76 | #endif 77 | 78 | if ((*type != 'r' && *type != 'w') || type[1]) 79 | return (NULL); 80 | 81 | if (!pids) { 82 | if ((fds = sysconf(_SC_OPEN_MAX)) <= 0) 83 | return (NULL); 84 | if (fds > MAX_CLOSE_FD) 85 | fds = MAX_CLOSE_FD; /* avoid allocating too much memory */ 86 | if (!(pids = (PID_T *) malloc((u_int) ((size_t)fds * sizeof (PID_T))))) 87 | return (NULL); 88 | memset((char *) pids, 0, (size_t)fds * sizeof (PID_T)); 89 | } 90 | if (pipe(pdes) < 0) 91 | return (NULL); 92 | if (pdes[0] >= fds || pdes[1] >= fds) { 93 | (void) close(pdes[0]); 94 | (void) close(pdes[1]); 95 | return NULL; 96 | } 97 | 98 | /* break up string into pieces */ 99 | for (argc = 0, cp = program; argc < MAX_ARGS; cp = NULL) 100 | if (!(argv[argc++] = strtok(cp, " \t\n"))) 101 | break; 102 | 103 | iop = NULL; 104 | switch (pid = fork()) { 105 | case -1: /* error */ 106 | (void) close(pdes[0]); 107 | (void) close(pdes[1]); 108 | goto pfree; 109 | /* NOTREACHED */ 110 | case 0: /* child */ 111 | if (*type == 'r') { 112 | if (pdes[1] != STDOUT) { 113 | dup2(pdes[1], STDOUT); 114 | dup2(pdes[1], STDERR); /* stderr, too! */ 115 | (void) close(pdes[1]); 116 | } 117 | (void) close(pdes[0]); 118 | } 119 | else { 120 | if (pdes[0] != STDIN) { 121 | dup2(pdes[0], STDIN); 122 | (void) close(pdes[0]); 123 | } 124 | (void) close(pdes[1]); 125 | } 126 | 127 | /* reset SIGPIPE to default for the child */ 128 | memset(&sa, 0, sizeof(sa)); 129 | sa.sa_handler = SIG_DFL; 130 | sigaction(SIGPIPE, &sa, NULL); 131 | 132 | /* close all unwanted open file descriptors */ 133 | for (fd = STDERR + 1; fd < fds; fd++) { 134 | close(fd); 135 | } 136 | 137 | if (cron_change_user_permanently(pw, env_get("HOME", jobenv)) != 0) 138 | _exit(2); 139 | 140 | if (execvpe(argv[0], argv, jobenv) < 0) { 141 | int save_errno = errno; 142 | 143 | log_it("CRON", getpid(), "EXEC FAILED", program, save_errno); 144 | if (*type != 'r') { 145 | while (0 != (out = read(STDIN, buf, PIPE_BUF))) { 146 | if ((out == -1) && (errno != EINTR)) 147 | break; 148 | } 149 | } 150 | } 151 | _exit(1); 152 | } 153 | /* parent; assume fdopen can't fail... */ 154 | if (*type == 'r') { 155 | fd = pdes[0]; 156 | iop = fdopen(pdes[0], type); 157 | (void) close(pdes[1]); 158 | } 159 | else { 160 | fd = pdes[1]; 161 | iop = fdopen(pdes[1], type); 162 | (void) close(pdes[0]); 163 | } 164 | pids[fd] = pid; 165 | 166 | pfree: 167 | return (iop); 168 | } 169 | 170 | static int cron_finalize(FILE * iop, int sig) { 171 | int fdes; 172 | sigset_t oset, nset; 173 | WAIT_T stat_loc; 174 | PID_T pid; 175 | 176 | /* 177 | * pclose returns -1 if stream is not associated with a 178 | * `popened' command, or, if already `pclosed'. 179 | */ 180 | fdes = fileno(iop); 181 | if (pids == NULL || fdes >= fds || pids[fdes] == 0L) 182 | return (-1); 183 | 184 | if (!sig) { 185 | (void) fclose(iop); 186 | } else if (kill(pids[fdes], sig) == -1) { 187 | return -1; 188 | } 189 | 190 | sigemptyset(&nset); 191 | sigaddset(&nset, SIGINT); 192 | sigaddset(&nset, SIGQUIT); 193 | sigaddset(&nset, SIGHUP); 194 | (void) sigprocmask(SIG_BLOCK, &nset, &oset); 195 | while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1) ; 196 | (void) sigprocmask(SIG_SETMASK, &oset, NULL); 197 | 198 | if (sig) { 199 | (void) fclose(iop); 200 | } 201 | pids[fdes] = 0; 202 | 203 | if (pid < 0) { 204 | return pid; 205 | } 206 | 207 | if (WIFEXITED(stat_loc)) { 208 | return WEXITSTATUS(stat_loc); 209 | } else { 210 | return WTERMSIG(stat_loc); 211 | } 212 | } 213 | 214 | int cron_pclose(FILE * iop) { 215 | return cron_finalize(iop, 0); 216 | } 217 | 218 | int cron_pabort(FILE * iop) { 219 | int esig = cron_finalize(iop, SIGKILL); 220 | return esig == SIGKILL ? 0 : esig; 221 | } 222 | -------------------------------------------------------------------------------- /src/pw_dup.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2000,2002 Todd C. Miller 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL 9 | * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 10 | * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE 11 | * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 13 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* 18 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 19 | * 20 | * Permission to use, copy, modify, and distribute this software for any 21 | * purpose with or without fee is hereby granted, provided that the above 22 | * copyright notice and this permission notice appear in all copies. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 25 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 26 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 27 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 28 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 29 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 30 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 31 | */ 32 | 33 | #include "config.h" 34 | 35 | #include 36 | 37 | #if !defined(OpenBSD) || OpenBSD < 200105 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include "funcs.h" 45 | #include "globals.h" 46 | 47 | struct passwd * 48 | pw_dup(const struct passwd *pw) { 49 | char *cp; 50 | size_t nsize=0, psize=0, gsize=0, dsize=0, ssize=0, total; 51 | struct passwd *newpw; 52 | 53 | /* Allocate in one big chunk for easy freeing */ 54 | total = sizeof(struct passwd); 55 | if (pw->pw_name) { 56 | nsize = strlen(pw->pw_name) + 1; 57 | total += nsize; 58 | } 59 | if (pw->pw_passwd) { 60 | psize = strlen(pw->pw_passwd) + 1; 61 | total += psize; 62 | } 63 | #ifdef LOGIN_CAP 64 | if (pw->pw_class) { 65 | csize = strlen(pw->pw_class) + 1; 66 | total += csize; 67 | } 68 | #endif /* LOGIN_CAP */ 69 | if (pw->pw_gecos) { 70 | gsize = strlen(pw->pw_gecos) + 1; 71 | total += gsize; 72 | } 73 | if (pw->pw_dir) { 74 | dsize = strlen(pw->pw_dir) + 1; 75 | total += dsize; 76 | } 77 | if (pw->pw_shell) { 78 | ssize = strlen(pw->pw_shell) + 1; 79 | total += ssize; 80 | } 81 | if ((cp = malloc(total)) == NULL) 82 | return (NULL); 83 | newpw = (struct passwd *)cp; 84 | 85 | /* 86 | * Copy in passwd contents and make strings relative to space 87 | * at the end of the buffer. 88 | */ 89 | (void)memcpy(newpw, pw, sizeof(struct passwd)); 90 | cp += sizeof(struct passwd); 91 | if (pw->pw_name) { 92 | (void)memcpy(cp, pw->pw_name, nsize); 93 | newpw->pw_name = cp; 94 | cp += nsize; 95 | } 96 | if (pw->pw_passwd) { 97 | (void)memcpy(cp, pw->pw_passwd, psize); 98 | newpw->pw_passwd = cp; 99 | cp += psize; 100 | } 101 | #ifdef LOGIN_CAP 102 | if (pw->pw_class) { 103 | (void)memcpy(cp, pw->pw_class, csize); 104 | newpw->pw_class = cp; 105 | cp += csize; 106 | } 107 | #endif /* LOGIN_CAP */ 108 | if (pw->pw_gecos) { 109 | (void)memcpy(cp, pw->pw_gecos, gsize); 110 | newpw->pw_gecos = cp; 111 | cp += gsize; 112 | } 113 | if (pw->pw_dir) { 114 | (void)memcpy(cp, pw->pw_dir, dsize); 115 | newpw->pw_dir = cp; 116 | cp += dsize; 117 | } 118 | if (pw->pw_shell) { 119 | (void)memcpy(cp, pw->pw_shell, ssize); 120 | newpw->pw_shell = cp; 121 | cp += ssize; 122 | } 123 | 124 | /* cppcheck-suppress[memleak symbolName=cp] memory originally pointed to by cp returned via newpw */ 125 | return (newpw); 126 | } 127 | 128 | #endif /* !OpenBSD || OpenBSD < 200105 */ 129 | -------------------------------------------------------------------------------- /src/structs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id: structs.h,v 1.7 2004/01/23 18:56:43 vixie Exp $ 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 7 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | #ifndef CRONIE_STRUCTS_H 23 | #define CRONIE_STRUCTS_H 24 | 25 | #include 26 | #include 27 | #ifdef WITH_SELINUX 28 | #include 29 | #endif 30 | #include "macros.h" 31 | #include "bitstring.h" 32 | 33 | typedef struct _entry { 34 | struct _entry *next; 35 | struct passwd *pwd; 36 | char **envp; 37 | char *cmd; 38 | bitstr_t bit_decl(minute, MINUTE_COUNT); 39 | bitstr_t bit_decl(hour, HOUR_COUNT); 40 | bitstr_t bit_decl(dom, DOM_COUNT); 41 | bitstr_t bit_decl(month, MONTH_COUNT); 42 | bitstr_t bit_decl(dow, DOW_COUNT); 43 | int flags; 44 | int delay; 45 | #define MIN_STAR 0x01 46 | #define HR_STAR 0x02 47 | #define DOM_STAR 0x04 48 | #define DOW_STAR 0x08 49 | #define WHEN_REBOOT 0x10 50 | #define DONT_LOG 0x20 51 | #define MAIL_WHEN_ERR 0x40 52 | } entry; 53 | 54 | /* the crontab database will be a list of the 55 | * following structure, one element per user 56 | * plus one for the system. 57 | * 58 | * These are the crontabs. 59 | */ 60 | #ifndef WITH_SELINUX 61 | #define security_context_t unsigned 62 | #endif 63 | 64 | typedef struct _user { 65 | struct _user *next, *prev; /* links */ 66 | char *name; 67 | char *tabname; /* /etc/cron.d/ file name or NULL */ 68 | time_t mtime; /* last modtime of crontab */ 69 | entry *crontab; /* this person's crontab */ 70 | security_context_t scontext; /* SELinux security context */ 71 | int system; /* is it a system crontab */ 72 | } user; 73 | 74 | typedef struct _orphan { 75 | struct _orphan *next; /* link */ 76 | char *uname; 77 | char *fname; 78 | char *tabname; 79 | } orphan; 80 | 81 | typedef struct _cron_db { 82 | user *head, *tail; /* links */ 83 | time_t mtime; /* last modtime on spooldir */ 84 | #ifdef WITH_INOTIFY 85 | int ifd; 86 | #endif 87 | } cron_db; 88 | /* in the C tradition, we only create 89 | * variables for the main program, just 90 | * extern them elsewhere. 91 | */ 92 | 93 | #endif /* CRONIE_STRUCTS_H */ 94 | -------------------------------------------------------------------------------- /src/user.c: -------------------------------------------------------------------------------- 1 | /* Copyright 1988,1990,1993,1994 by Paul Vixie 2 | * All rights reserved 3 | */ 4 | 5 | /* 6 | * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") 7 | * Copyright (c) 1997,2000 by Internet Software Consortium, Inc. 8 | * 9 | * Permission to use, copy, modify, and distribute this software for any 10 | * purpose with or without fee is hereby granted, provided that the above 11 | * copyright notice and this permission notice appear in all copies. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 19 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 | */ 21 | 22 | /* vix 26jan87 [log is in RCS file] 23 | */ 24 | 25 | #include "config.h" 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "funcs.h" 33 | #include "globals.h" 34 | 35 | static const char *FileName; 36 | 37 | static void 38 | log_error (const char *msg) 39 | { 40 | log_it ("CRON", getpid (), msg, FileName, 0); 41 | } 42 | 43 | void 44 | free_user (user * u) { 45 | entry *e, *ne; 46 | 47 | if (!u) { 48 | return; 49 | } 50 | 51 | free(u->name); 52 | free(u->tabname); 53 | for (e = u->crontab; e != NULL; e = ne) { 54 | ne = e->next; 55 | free_entry(e); 56 | } 57 | #ifdef WITH_SELINUX 58 | free_security_context(&(u->scontext)); 59 | #endif 60 | free(u); 61 | } 62 | 63 | user * 64 | load_user (int crontab_fd, struct passwd *pw, const char *uname, 65 | const char *fname, const char *tabname) { 66 | char envstr[MAX_ENVSTR]; 67 | FILE *file; 68 | user *u; 69 | entry *e; 70 | int status = TRUE, save_errno = 0; 71 | char **envp = NULL, **tenvp; 72 | int envs = 0, entries = 0; 73 | 74 | if (!(file = fdopen(crontab_fd, "r"))) { 75 | save_errno = errno; 76 | log_it(uname, getpid (), "FAILED", "fdopen on crontab_fd in load_user", 77 | save_errno); 78 | close(crontab_fd); 79 | return (NULL); 80 | } 81 | 82 | Debug(DPARS, ("load_user()\n")); 83 | /* file is open. build user entry, then read the crontab file. 84 | */ 85 | if ((u = (user *) malloc (sizeof (user))) == NULL) { 86 | save_errno = errno; 87 | goto done; 88 | } 89 | memset(u, 0, sizeof(*u)); 90 | 91 | if (((u->name = strdup(fname)) == NULL) 92 | || ((u->tabname = strdup(tabname)) == NULL)) { 93 | save_errno = errno; 94 | goto done; 95 | } 96 | 97 | u->system = pw == NULL; 98 | 99 | /* init environment. this will be copied/augmented for each entry. 100 | */ 101 | if ((envp = env_init()) == NULL) { 102 | save_errno = errno; 103 | goto done; 104 | } 105 | 106 | if (env_set_from_environ(&envp) == FALSE) { 107 | save_errno = errno; 108 | goto done; 109 | } 110 | 111 | #ifdef WITH_SELINUX 112 | if (get_security_context(pw == NULL ? NULL : uname, 113 | crontab_fd, &u->scontext, tabname) != 0) { 114 | goto done; 115 | } 116 | #endif 117 | /* load the crontab 118 | */ 119 | while (status >= OK) { 120 | if (!skip_comments(file) && !u->system) { 121 | log_error("too many garbage characters"); 122 | status = TRUE; 123 | break; 124 | } 125 | status = load_env (envstr, file); 126 | switch (status) { 127 | case ERR: 128 | /* If envstr has content, we reached EOF 129 | * without a newline, and the line will be 130 | * ignored. 131 | */ 132 | if (envstr[0] != '\0') { 133 | FileName = tabname; 134 | log_error("missing newline before EOF"); 135 | } 136 | break; 137 | case FALSE: 138 | ++entries; 139 | if (!u->system && entries > MAX_USER_ENTRIES) { 140 | log_error("too many entries"); 141 | status = TRUE; 142 | goto done; 143 | } 144 | FileName = tabname; 145 | e = load_entry(file, log_error, pw, envp); 146 | if (e) { 147 | e->next = u->crontab; 148 | u->crontab = e; 149 | } 150 | break; 151 | case TRUE: 152 | ++envs; 153 | if (!u->system && envs > MAX_USER_ENVS) { 154 | log_error("too many environment variables"); 155 | goto done; 156 | } 157 | if ((tenvp = env_set (envp, envstr)) == NULL) { 158 | save_errno = errno; 159 | goto done; 160 | } 161 | envp = tenvp; 162 | break; 163 | } 164 | } 165 | 166 | done: 167 | if (status == TRUE) { 168 | log_it(uname, getpid(), "FAILED", "loading cron table", 169 | save_errno); 170 | free_user(u); 171 | u = NULL; 172 | } 173 | if (envp) 174 | env_free(envp); 175 | fclose(file); 176 | Debug(DPARS, ("...load_user() done\n")); 177 | errno = save_errno; 178 | return (u); 179 | } 180 | --------------------------------------------------------------------------------