write "fake" timestamp file when none exists yet
[yacron.git] / main.c
blobd4f38fb18265bd5a25554b4b4a619158ce399d51
2 /*
3 * MAIN.C
5 * crond [-l#] [-L logfile | -S ] [-M mailer] [-m mailto] [-d|-f|-b] [-c crondir] [-s systemdir] [-t timestamps]
7 * run as root, but NOT setuid root
9 * Copyright 1994 Matthew Dillon (dillon@apollo.backplane.com)
10 * Copyright 2009 James Pryor <profjim@jimpryor.net>
11 * May be distributed under the GNU General Public License
14 #include "defs.h"
16 Prototype short DebugOpt;
17 Prototype short LogLevel;
18 Prototype short ForegroundOpt;
19 Prototype short LoggerOpt;
20 Prototype const char *CDir;
21 Prototype const char *SCDir;
22 Prototype const char *TSDir;
23 Prototype const char *LogFile;
24 Prototype uid_t DaemonUid;
25 Prototype int InSyncFileRoot;
26 Prototype const char *SendMail;
27 Prototype const char *Mailto;
28 Prototype char *TempDir;
29 Prototype char *TempFileFmt;
31 short DebugOpt;
32 short LogLevel = LOG_NOTICE;
33 short ForegroundOpt = 0;
34 short LoggerOpt;
35 const char *CDir = CRONTABS;
36 const char *SCDir = SCRONTABS;
37 const char *TSDir = TIMESTAMPS;
38 const char *LogFile = LOG_FILE; /* opened with mode 0600 */
39 const char *SendMail = NULL;
40 const char *Mailto = NULL;
41 char *TempDir;
42 char *TempFileFmt;
44 uid_t DaemonUid;
45 int InSyncFileRoot;
47 int
48 main(int ac, char **av)
50 const char *LevelAry[] = {
51 "emerg",
52 "alert",
53 "crit",
54 "err",
55 "warning",
56 "notice",
57 "info",
58 "debug",
59 "panic",
60 "error",
61 "warn",
62 NULL
64 int i;
67 * parse options
70 DaemonUid = getuid();
72 opterr = 0;
74 while ((i = getopt(ac,av,"dl:L:fbSc:s:m:M:t:")) != EOF) {
75 switch (i) {
76 case 'l':
78 char *ptr;
79 int j;
80 ptr = optarg;
81 for (j = 0; LevelAry[j]; ++j) {
82 if (strncmp(ptr, LevelAry[j], strlen(LevelAry[j])) == 0) {
83 break;
86 switch(j) {
87 case 0:
88 case 8:
89 /* #define LOG_EMERG 0 [* system is unusable *] */
90 LogLevel = LOG_EMERG;
91 break;
92 case 1:
93 /* #define LOG_ALERT 1 [* action must be taken immediately *] */
94 LogLevel = LOG_ALERT;
95 break;
96 case 2:
97 /* #define LOG_CRIT 2 [* critical conditions *] */
98 LogLevel = LOG_CRIT;
99 break;
100 case 3:
101 case 9:
102 /* #define LOG_ERR 3 [* error conditions *] */
103 LogLevel = LOG_ERR;
104 break;
105 case 4:
106 case 10:
107 /* #define LOG_WARNING 4 [* warning conditions *] */
108 LogLevel = LOG_WARNING;
109 break;
110 case 5:
111 /* #define LOG_NOTICE 5 [* normal but significant condition *] */
112 LogLevel = LOG_NOTICE;
113 break;
114 case 6:
115 /* #define LOG_INFO 6 [* informational *] */
116 LogLevel = LOG_INFO;
117 break;
118 case 7:
119 /* #define LOG_DEBUG 7 [* debug-level messages *] */
120 LogLevel = LOG_DEBUG;
121 break;
122 default:
123 LogLevel = atoi(optarg);
126 break;
127 case 'd':
128 DebugOpt = 1;
129 LogLevel = LOG_DEBUG;
130 /* fall through to include f too */
131 case 'f':
132 ForegroundOpt = 1;
133 break;
134 case 'b':
135 ForegroundOpt = 0;
136 break;
137 case 'S': /* log through syslog */
138 LoggerOpt = 0;
139 break;
140 case 'L': /* use internal log formatter */
141 LoggerOpt = 1;
142 if (*optarg != 0) {
143 LogFile = optarg;
145 break;
146 case 'c':
147 if (*optarg != 0) CDir = optarg;
148 break;
149 case 's':
150 if (*optarg != 0) SCDir = optarg;
151 break;
152 case 't':
153 if (*optarg != 0) TSDir = optarg;
154 break;
155 case 'M':
156 if (*optarg != 0) SendMail = optarg;
157 break;
158 case 'm':
159 if (*optarg != 0) Mailto = optarg;
160 break;
161 default:
163 * check for parse error
165 printf("dcron " VERSION "\n");
166 printf("crond [-l#] [-L logfile | -S ] [-M mailer] [-m mailto] [-d|-f|-b] [-c crondir] [-s systemdir] [-t timestamps]\n");
167 printf("-l num\tlogging level (default <= LOG_NOTICE = 5)\n");
168 printf("-L file\tlog to file (default %s)\n-S\tlog to syslogd (default)\n", LOG_FILE);
169 printf("-M mailer\tprogram to mail output (default %s)\n-m mailto\taddress to mail cron output to (default to user)\n", SENDMAIL);
170 printf("-d\tdebugging\n-f\trun in foreground\n-b\trun in background (default)\n");
171 printf("-c crondir\tcrontab spool dir (default %s)\n-s systemdir\tsystem cron.d dir (default %s)\n-t timestamps\ttimestamp dir (default %s)\n",
172 CRONTABS, SCRONTABS, TIMESTAMPS);
173 exit(2);
178 * close stdin and stdout.
179 * close unused descriptors - don't need.
180 * optional detach from controlling terminal
183 fclose(stdin);
184 fclose(stdout);
186 for (i = 3; i < OPEN_MAX; ++i) {
187 close(i);
190 i = open("/dev/null", O_RDWR);
191 if (i < 0) {
192 perror("open: /dev/null");
193 exit(1);
195 dup2(i, 0);
196 dup2(i, 1);
198 if (ForegroundOpt == 0) {
200 fclose(stderr);
201 dup2(i, 2);
203 int fd;
204 int pid;
205 if (setsid() < 0)
206 perror("setsid");
208 if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
209 ioctl(fd, TIOCNOTTY, 0);
210 close(fd);
213 pid = fork();
215 if (pid < 0) {
216 perror("fork");
217 exit(1);
219 if (pid > 0)
220 exit(0);
223 (void)startlogger(); /* need if syslog mode selected */
224 (void)initsignals(); /* set some signal handlers */
226 /* create tempdir with permissions 755 for cron output */
227 TempDir = strdup(TMPDIR "/cron.XXXXXX");
228 if (mkdtemp(TempDir) == NULL) {
229 perror("mkdtemp");
230 exit(1);
232 if (chmod(TempDir, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)) {
233 perror("chmod");
234 exit(1);
236 asprintf(&TempFileFmt, "%s/cron.%%s.%%d", TempDir);
239 * main loop - synchronize to 1 second after the minute, minimum sleep
240 * of 1 second.
243 logn(LOG_INFO,"%s " VERSION " yacron, started with loglevel %s\n", av[0], LevelAry[LogLevel]);
244 SynchronizeDir(CDir, NULL, 1);
245 SynchronizeDir(SCDir, "root", 1);
246 ReadTimestamps(NULL);
247 TestStartupJobs(); /* @startup jobs only run when crond is started, not when their crontab is loaded */
250 time_t t1 = time(NULL);
251 time_t t2;
252 long dt;
253 short rescan = 60;
254 short stime = 60;
256 for (;;) {
257 sleep((stime + 1) - (short)(time(NULL) % stime));
259 t2 = time(NULL);
260 dt = t2 - t1;
263 * The file 'cron.update' is checked to determine new cron
264 * jobs. The directory is rescanned once an hour to deal
265 * with any screwups.
267 * check for disparity. Disparities over an hour either way
268 * result in resynchronization. A reverse-indexed disparity
269 * less then an hour causes us to effectively sleep until we
270 * match the original time (i.e. no re-execution of jobs that
271 * have just been run). A forward-indexed disparity less then
272 * an hour causes intermediate jobs to be run, but only once
273 * in the worst case.
275 * when running jobs, the inequality used is greater but not
276 * equal to t1, and less then or equal to t2.
279 if (--rescan == 0) {
280 rescan = 60;
281 SynchronizeDir(CDir, NULL, 0);
282 SynchronizeDir(SCDir, "root", 0);
283 ReadTimestamps(NULL);
284 } else {
285 CheckUpdates(CDir, NULL, t1, t2);
286 CheckUpdates(SCDir, "root", t1, t2);
288 if (DebugOpt)
289 logn(LOG_DEBUG, "Wakeup dt=%d\n", dt);
290 if (dt < -60*60 || dt > 60*60) {
291 t1 = t2;
292 logn(LOG_NOTICE,"time disparity of %d minutes detected\n", dt / 60);
293 } else if (dt > 0) {
294 TestJobs(t1, t2);
295 RunJobs();
296 sleep(5);
297 if (CheckJobs() > 0)
298 stime = 10;
299 else
300 stime = 60;
301 t1 = t2;
305 /* not reached */