1 /*
2 * FCRON - periodic command scheduler
3 *
4 * Copyright 2000-2016 Thibault Godouet <fcron@free.fr>
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
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * The GNU General Public License can also be found in the file
21 * `LICENSE' that comes with the fcron source distribution.
22 */
23
24
25 #include "fcrontab.h"
26
27 #include "fileconf.h"
28
29 char *get_string(char *ptr);
30 int get_line(char *str, size_t size, FILE * file);
31 void init_default_line(cl_t * cl, cf_t * cf);
32 char *get_time(char *ptr, time_t * time, int zero_allowed);
33 char *get_num(char *ptr, int *num, int max, short int decimal,
34 const char **names);
35 char *get_nice(char *ptr, int *nice);
36 char *get_bool(char *ptr, int *i);
37 char *read_field(char *ptr, bitstr_t * ary, int max, const char **names);
38 void read_freq(char *ptr, cf_t * cf);
39 void read_arys(char *ptr, cf_t * cf);
40 void read_period(char *ptr, cf_t * cf);
41 int read_shortcut(char *ptr, cf_t * cf);
42 void read_env(char *ptr, cf_t * cf);
43 char *read_opt(char *ptr, cl_t * cl);
44 char *check_username(char *ptr, cf_t * cf, cl_t * cl);
45 size_t option_strlen(char *value);
46
47 char need_correction;
48 cl_t default_line; /* default options for a line */
49 char *file_name;
50 int line;
51
52 /* warning : all names must have the same length */
53 const char *dows_ary[] = {
54 "sun", "mon", "tue", "wed", "thu", "fri", "sat",
55 NULL
56 };
57
58 /* warning : all names must have the same length */
59 const char *mons_ary[] = {
60 "jan", "feb", "mar", "apr", "may", "jun",
61 "jul", "aug", "sep", "oct", "nov", "dec",
62 NULL
63 };
64
65
66 #define GET_LINE_EOF 999
67
68 char *
get_string(char * ptr)69 get_string(char *ptr)
70 /* read string pointed by ptr, remove blanks and manage
71 * string placed in quotes */
72 /* return NULL on mismatched quotes */
73 {
74 char quote = 0;
75 int length = 0;
76 char *rtn_string = NULL;
77
78 if (*ptr == '\"' || *ptr == '\'') {
79 quote = *ptr;
80 ptr++;
81 }
82
83 length = remove_blanks(ptr);
84
85 if (quote != 0) {
86 if (*(ptr + length - 1) == quote)
87 *(ptr + length - 1) = '\0';
88 else {
89 /* mismatched quotes */
90 need_correction = 1;
91 return NULL;
92 }
93 }
94
95 Set(rtn_string, ptr);
96 return rtn_string;
97
98 }
99
100
101 int
get_line(char * str,size_t size,FILE * file)102 get_line(char *str, size_t size, FILE * file)
103 /* similar to fgets, but increase line if necessary,
104 * and continue over an "\" followed by an "\n" char */
105 {
106 size_t size_max = size - 1;
107 int i = 0;
108 int c;
109
110 while (i < size_max) {
111
112 switch (c = getc(file)) {
113
114 case '\n':
115 /* check if the \n char is preceded by a "\" char :
116 * in this case, suppress the "\", don't copy the \n,
117 * and continue */
118 if (i > 0 && *(str + i - 1) == '\\') {
119 i--;
120 line++;
121 continue;
122 }
123 else {
124 *(str + i) = (char)'\0';
125 return OK;
126 }
127 break;
128
129 case EOF:
130 *(str + i) = (char)'\0';
131 /* we couldn't return EOF ( equal to ERR by default )
132 * nor ERR, which is used for another error */
133 return GET_LINE_EOF;
134
135 default:
136 *(str + i) = (char)c;
137 i++;
138
139 }
140
141 }
142
143 /* line is too long : goto next line and return ERR */
144 while (((c = getc(file)) != EOF) && (c != '\n')) ;
145 line++;
146 need_correction = 1;
147 return ERR;
148
149 }
150
151 void
init_default_line(cl_t * cl,cf_t * cf)152 init_default_line(cl_t * cl, cf_t * cf)
153 /* clear all context/options from cl */
154 {
155 bzero(cl, sizeof(cl_t));
156 Set(cl->cl_runas, runas);
157 Set(cl->cl_mailto, runas);
158 Free_safe(cl->cl_tz);
159 set_default_opt(cl->cl_option);
160 cl->cl_file = cf;
161 }
162
163
164 int
read_file(char * filename,int fd)165 read_file(char *filename, int fd)
166 /* read file "name" and append cf_t list */
167 {
168 cf_t *cf = NULL;
169 FILE *file = NULL;
170 char buf[LINE_LEN];
171 int max_lines;
172 int max_entries = MAXENTRIES;
173 int entries = 0;
174 char *ptr = NULL;
175 int ret;
176
177 bzero(buf, sizeof(buf));
178 need_correction = 0;
179 line = 1;
180 file_name = filename;
181
182 /* open file */
183
184 if ((file = fdopen(fd, "r")) == NULL) {
185 fprintf(stderr, "Could not open \"%s\": %s\n", file_name,
186 strerror(errno));
187 return ERR;
188 }
189
190 /* Rewind, just in case */
191 rewind(file);
192
193 Alloc(cf, cf_t);
194 cf->cf_env_list = env_list_init();
195 Set(cf->cf_user, user);
196 init_default_line(&default_line, cf);
197
198 if (debug_opt)
199 fprintf(stderr, "FILE %s\n", file_name);
200
201 if (strcmp(runas, ROOTNAME) == 0)
202 max_entries = 65535;
203
204 /* max_lines acts here as a security counter to avoid endless loop. */
205 max_lines = (max_entries * 10) + 10;
206
207 while (entries < max_entries && line <= max_lines) {
208
209 ret = get_line(buf, sizeof(buf), file);
210
211 if (ret == ERR) {
212 fprintf(stderr,
213 "%s:%d: Line is too long (more than %d): skipping line.\n",
214 file_name, line, (int)sizeof(buf));
215 continue;
216 }
217
218 ptr = buf;
219 Skip_blanks(ptr);
220
221 if (debug_opt && *ptr != '#' && *ptr != '\0')
222 fprintf(stderr, " %s\n", buf);
223
224 switch (*ptr) {
225 case '#':
226 case '\0':
227 /* comments or empty line: skipping */
228 break;
229 case '@':
230 /* if it is not a shortcut line then read_shortcut() won't do anything. */
231 if (!read_shortcut(ptr, cf))
232 read_freq(ptr, cf);
233 entries++;
234 break;
235 case '&':
236 read_arys(ptr, cf);
237 entries++;
238 break;
239 case '%':
240 read_period(ptr, cf);
241 entries++;
242 break;
243 case '!':
244 ptr = read_opt(ptr, &default_line);
245 if (ptr != NULL && *ptr != '\0') {
246 fprintf(stderr, "%s:%d: Syntax error: string \"%s\" ignored\n",
247 file_name, line, ptr);
248 need_correction = 1;
249 }
250 break;
251 default:
252 if (isdigit((int)*ptr) || *ptr == '*') {
253 read_arys(ptr, cf);
254 entries++;
255 }
256 else
257 read_env(ptr, cf);
258 }
259
260 line++;
261
262 if (ret != OK)
263 /* in this case, ret == GET_LINE_EOF :
264 * no more lines, so we exit the loop */
265 break;
266
267 }
268
269 if (entries == max_entries) {
270 error("%s:%d: maximum number of entries (%d) has been reached by %s",
271 file_name, line, user);
272 fprintf(stderr, "Anything after this line will be ignored\n");
273 }
274 else if (line == max_lines)
275 error("%s:%d: maximum number of lines (%d) has been reached by %s",
276 file_name, line, user);
277
278 cf->cf_next = file_base;
279 file_base = cf;
280
281 Free_safe(default_line.cl_runas);
282 Free_safe(default_line.cl_mailfrom);
283 Free_safe(default_line.cl_mailto);
284 Free_safe(default_line.cl_tz);
285
286 if (!need_correction)
287 return OK;
288 else
289 return 2;
290
291 }
292
293 void
read_env(char * ptr,cf_t * cf)294 read_env(char *ptr, cf_t * cf)
295 /* append env variable list.
296 * (remove blanks) */
297 {
298 char name[LINE_LEN];
299 int j = 0;
300 char *val = NULL;
301
302 bzero(name, sizeof(name));
303
304 /* copy env variable's name */
305 while ((isalnum((int)*ptr) || *ptr == '_') && *ptr != '='
306 && !isspace((int)*ptr) && j < sizeof(name)) {
307 name[j++] = *ptr;
308 ptr++;
309 }
310 name[j] = '\0';
311
312 if (name[0] == '\0')
313 goto error;
314
315 /* skip '=' and spaces around */
316 while (isspace((int)*ptr))
317 ptr++;
318
319 /* if j == 0 name is a zero length string */
320 if (*ptr++ != '=' || j == 0)
321 goto error;
322
323 while (isspace((int)*ptr))
324 ptr++;
325
326 /* get value */
327 if ((val = get_string(ptr)) == NULL) {
328 fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
329 file_name, line);
330 need_correction = 1;
331 return;
332 }
333
334 if (debug_opt)
335 fprintf(stderr, " Env : '%s=%s'\n", name, val);
336
337 /* we ignore USER/LOGNAME's assignment */
338 if (strcmp(name, "USER") == 0 || strcmp(name, "LOGNAME") == 0) {
339 fprintf(stderr,
340 "%s:%d: USER or LOGNAME assignement is not allowed: ignored.\n",
341 file_name, line);
342 return;
343 }
344
345 /* the MAILFROM/MAILTO assignment is, in fact, an fcron option :
346 * we don't store it in the same way. */
347 /* please note that we check if the mailto is valid in conf.c */
348 if (strcmp(name, "MAILFROM") == 0) {
349 Set(default_line.cl_mailfrom, val);
350 }
351 else if (strcmp(name, "MAILTO") == 0) {
352 /* cl_mailto must not be NULL (as expected in
353 * conf.c:add_line_to_file()), so we check if the length is >= 0 */
354 if (strcmp(val, "\0") == 0) {
355 clear_mail(default_line.cl_option);
356 clear_mailzerolength(default_line.cl_option);
357 }
358 else {
359 Set(default_line.cl_mailto, val);
360 set_mail(default_line.cl_option);
361 }
362
363 }
364 else {
365 env_list_setenv(cf->cf_env_list, name, val, 1);
366 }
367
368 Free_safe(val);
369
370 return;
371
372 error:
373 fprintf(stderr, "%s:%d: Syntax error: skipping line.\n", file_name, line);
374 need_correction = 1;
375 return;
376
377 }
378
379
380 char *
get_nice(char * ptr,int * nice)381 get_nice(char *ptr, int *nice)
382 /* read a nice value and put it in variable nice */
383 {
384 char negative = 0;
385
386 if (*ptr == '-') {
387 negative = 1;
388 ptr++;
389 }
390
391 if ((ptr = get_num(ptr, nice, 20, 0, NULL)) == NULL)
392 return NULL;
393
394 if (negative == 1) {
395 if (getuid() != rootuid) {
396 fprintf(stderr, "must be privileged to use a negative argument "
397 "with nice: set to 0\n");
398 need_correction = 1;
399 *nice = 0;
400 }
401
402 *nice *= (-1);
403 }
404
405 return ptr;
406
407 }
408
409 size_t
option_strlen(char * value)410 option_strlen(char *value)
411 /* return the length of the string value of an option */
412 {
413 char *ptr = value;
414 size_t len = 0;
415
416 /* look for the end of the option value */
417 while (ptr != NULL && *ptr != ')' && *ptr != '\0') {
418 ptr++;
419 len++;
420 }
421
422 return len;
423 }
424
425 int
assign_option_string(char ** var,char * value)426 assign_option_string(char **var, char *value)
427 /* Read the value of an option: if non-empty, assign it to the var,
428 * otherwise set to NULL.
429 * Returns the length of the value (0 if empty), or -1 on error. */
430 {
431 char start = value[0];
432 char end = '\0';
433 size_t len = 0;
434
435 Free_safe(*var);
436
437 len = option_strlen(value);
438
439 if (len <= 0) {
440 return len;
441 }
442
443 end = value[len - 1];
444
445 /* spaces and quotes are not allowed before or after the value */
446 if (isspace((int)start) || isspace((int)end)
447 || start == '\'' || start == '"' || end == '\'' || end == '"') {
448 return -1;
449 }
450
451 *var = strndup2(value, len);
452
453 return len;
454 }
455
456
457 char *
get_bool(char * ptr,int * i)458 get_bool(char *ptr, int *i)
459 /* get a bool value : either true (1) or false (0)
460 * return NULL on error */
461 {
462 if (*ptr == '1')
463 goto true;
464 else if (*ptr == '0')
465 goto false;
466 else if (strncmp(ptr, "true", 4) == 0) {
467 ptr += 3;
468 goto true;
469 }
470 else if (strncmp(ptr, "yes", 3) == 0) {
471 ptr += 2;
472 goto true;
473 }
474 else if (strncmp(ptr, "false", 5) == 0) {
475 ptr += 4;
476 goto false;
477 }
478 else if (strncmp(ptr, "no", 2) == 0) {
479 ptr += 1;
480 goto false;
481 }
482 else
483 return NULL;
484
485 true:
486 *i = 1;
487 ptr++;
488 return ptr;
489
490 false:
491 *i = 0;
492 ptr++;
493 return ptr;
494
495 }
496
497
498 char *
read_opt(char * ptr,cl_t * cl)499 read_opt(char *ptr, cl_t * cl)
500 /* read one or several options and fill in the field "option" */
501 {
502 char opt_name[20];
503 int i;
504 char in_brackets;
505
506 #define Handle_err \
507 { \
508 fprintf(stderr, "%s:%d: Argument(s) for option \"%s\" not valid: " \
509 "skipping end of line.\n", file_name, line, opt_name); \
510 need_correction = 1; \
511 return NULL; \
512 }
513
514 if (*ptr == '!')
515 ptr++;
516
517 do {
518 i = 0;
519 bzero(opt_name, sizeof(opt_name));
520
521 while (isalnum((int)*ptr) && i < sizeof(opt_name))
522 opt_name[i++] = *ptr++;
523
524 i = 1;
525 in_brackets = 0;
526
527 if (*ptr == '(') {
528 in_brackets = 1;
529 ptr++;
530
531 /* spaces are not allowed -- make sure there is no leading space. */
532 if (isspace((int)*ptr)) {
533 Handle_err;
534 }
535 }
536
537 /* global options for a file */
538
539 if (strcmp(opt_name, "tzdiff") == 0) {
540 char negative = 0;
541
542 if (!in_brackets)
543 Handle_err;
544 if (*ptr == '-') {
545 negative = 1;
546 ptr++;
547 }
548 if ((ptr = get_num(ptr, &i, 24, 0, NULL)) == NULL)
549 Handle_err;
550 if (negative)
551 cl->cl_file->cf_tzdiff = (-i);
552 else
553 cl->cl_file->cf_tzdiff = i;
554
555 if (debug_opt)
556 fprintf(stderr, " Opt : \"%s\" (-)%d\n", opt_name, i);
557 }
558
559 /* options related to a line (or a set of lines) */
560
561 else if (strcmp(opt_name, "timezone") == 0) {
562 int len = -1;
563
564 if (!in_brackets) {
565 Handle_err;
566 }
567
568 /* assign_option_string() will set cl_tz to NULL is the value is empty,
569 * which means "use the system timezone" */
570 len = assign_option_string(&(cl->cl_tz), ptr);
571 if (len < 0) {
572 Handle_err;
573 }
574 else {
575 ptr += len;
576 }
577
578 if (debug_opt) {
579 fprintf(stderr, " Opt : \"%s\" \"%s\"\n", opt_name, cl->cl_tz);
580 }
581 }
582
583
584 else if (strcmp(opt_name, "s") == 0 || strcmp(opt_name, "serial") == 0) {
585 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
586 Handle_err;
587 if (i == 0)
588 clear_serial(cl->cl_option);
589 else
590 set_serial(cl->cl_option);
591 if (debug_opt)
592 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
593 }
594
595 else if (strcmp(opt_name, "serialonce") == 0) {
596 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
597 Handle_err;
598 if (i == 0)
599 set_serial_sev(cl->cl_option);
600 else
601 clear_serial_sev(cl->cl_option);
602 if (debug_opt)
603 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
604 }
605
606 else if (strcmp(opt_name, "lavgonce") == 0) {
607 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
608 Handle_err;
609 if (i == 0)
610 set_lavg_sev(cl->cl_option);
611 else
612 clear_lavg_sev(cl->cl_option);
613 if (debug_opt)
614 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
615 }
616
617 else if (strcmp(opt_name, "exesev") == 0) {
618 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
619 Handle_err;
620 if (i == 0)
621 clear_exe_sev(cl->cl_option);
622 else
623 set_exe_sev(cl->cl_option);
624 if (debug_opt)
625 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
626 }
627
628 else if (strcmp(opt_name, "b") == 0 || strcmp(opt_name, "bootrun") == 0) {
629 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
630 Handle_err;
631 if (i == 0)
632 clear_bootrun(cl->cl_option);
633 else
634 set_bootrun(cl->cl_option);
635 if (debug_opt)
636 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
637 }
638
639 else if (strcmp(opt_name, "rebootreset") == 0) {
640 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
641 Handle_err;
642 if (i == 0)
643 clear_rebootreset(cl->cl_option);
644 else
645 set_rebootreset(cl->cl_option);
646 if (debug_opt)
647 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
648 }
649
650
651 else if (strcmp(opt_name, "runatreboot") == 0) {
652 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
653 Handle_err;
654 if (i == 0)
655 clear_runatreboot(cl->cl_option);
656 else
657 set_runatreboot(cl->cl_option);
658 if (debug_opt)
659 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
660 }
661
662
663 else if (strcmp(opt_name, "runonce") == 0) {
664 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
665 Handle_err;
666 if (i == 0)
667 clear_runonce(cl->cl_option);
668 else
669 set_runonce(cl->cl_option);
670 if (debug_opt)
671 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
672 }
673
674 else if (strcmp(opt_name, "reset") == 0) {
675 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
676 Handle_err;
677 if (i == 1) {
678 init_default_line(cl, cl->cl_file);
679 }
680 if (debug_opt)
681 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
682 }
683
684 else if (strcmp(opt_name, "f") == 0 || strcmp(opt_name, "first") == 0) {
685 if (!in_brackets
686 || (ptr = get_time(ptr, &(cl->cl_first), 1)) == NULL)
687 Handle_err;
688 if (debug_opt)
689 fprintf(stderr, " Opt : \"%s\" %ld\n", opt_name,
690 (long int)cl->cl_first);
691 }
692
693 else if (strcmp(opt_name, "r") == 0 || strcmp(opt_name, "runfreq") == 0) {
694 if (cl->cl_runfreq == 1) {
695 fprintf(stderr, "cannot change runfreq value in a %%-line");
696 Handle_err;
697 }
698 if (!in_brackets
699 || (ptr = get_num(ptr, &i, USHRT_MAX, 0, NULL)) == NULL)
700 Handle_err;
701 if (i <= 1) {
702 fprintf(stderr, "runfreq must be 2 or more.\n");
703 Handle_err;
704 }
705 cl->cl_runfreq = i;
706 if (debug_opt)
707 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
708 }
709
710 else if (strcmp(opt_name, "strict") == 0) {
711 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
712 Handle_err;
713 if (i == 0)
714 clear_strict(cl->cl_option);
715 else
716 set_strict(cl->cl_option);
717 if (debug_opt)
718 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
719 }
720
721 else if (strcmp(opt_name, "noticenotrun") == 0) {
722 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
723 Handle_err;
724 if (i == 0)
725 clear_notice_notrun(cl->cl_option);
726 else
727 set_notice_notrun(cl->cl_option);
728 if (debug_opt)
729 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
730 }
731
732 else if (strcmp(opt_name, "lavg") == 0) {
733 if (!in_brackets
734 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
735 Handle_err;
736 cl->cl_lavg[0] = i;
737 if (debug_opt)
738 fprintf(stderr, " Opt : 'lavg1' %d\n", i);
739 if (*ptr++ != ',')
740 Handle_err;
741 if (!in_brackets
742 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
743 Handle_err;
744 cl->cl_lavg[1] = i;
745 if (debug_opt)
746 fprintf(stderr, " Opt : 'lavg5' %d\n", i);
747 if (*ptr++ != ',')
748 Handle_err;
749 if (!in_brackets
750 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
751 Handle_err;
752 cl->cl_lavg[2] = i;
753 if (debug_opt)
754 fprintf(stderr, " Opt : 'lavg15' %d\n", i);
755 if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
756 set_lavg(cl->cl_option);
757 else
758 clear_lavg(cl->cl_option);
759 #ifdef NOLOADAVG
760 warn("As fcron has been compiled with no procfs support,\n"
761 "you will not be able to use the lavg* options");
762 #endif /* NOLOADAVG */
763 }
764
765 else if (strcmp(opt_name, "lavg1") == 0) {
766 if (!in_brackets
767 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
768 Handle_err;
769 cl->cl_lavg[0] = i;
770 if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
771 set_lavg(cl->cl_option);
772 else
773 clear_lavg(cl->cl_option);
774 #if NOLOADAVG
775 warn("As fcron has been compiled with no procfs support,\n"
776 "you will not be able to use the lavg* options");
777 #endif /* NOLOADAVG */
778 if (debug_opt)
779 fprintf(stderr, " Opt : 'lavg1' %d\n", i);
780 }
781
782 else if (strcmp(opt_name, "lavg5") == 0) {
783 if (!in_brackets
784 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
785 Handle_err;
786 cl->cl_lavg[1] = i;
787 if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
788 set_lavg(cl->cl_option);
789 else
790 clear_lavg(cl->cl_option);
791 #ifdef NOLOADAVG
792 warn("As fcron has been compiled with no procfs support,\n"
793 "you will not be able to use the lavg* options");
794 #endif /* NOLOADAVG = 0 */
795 if (debug_opt)
796 fprintf(stderr, " Opt : 'lavg5' %d\n", i);
797 }
798
799 else if (strcmp(opt_name, "lavg15") == 0) {
800 if (!in_brackets
801 || (ptr = get_num(ptr, &i, UCHAR_MAX, 1, NULL)) == NULL)
802 Handle_err;
803 cl->cl_lavg[2] = i;
804 if (cl->cl_lavg[0] || cl->cl_lavg[1] || cl->cl_lavg[2])
805 set_lavg(cl->cl_option);
806 else
807 clear_lavg(cl->cl_option);
808 #ifdef NOLOADAVG
809 warn("As fcron has been compiled with no procfs support,\n"
810 "you will not be able to use the lavg* options");
811 #endif /* NOLOADAVG = 0 */
812 if (debug_opt)
813 fprintf(stderr, " Opt : 'lavg15' %d\n", i);
814 }
815
816 else if (strcmp(opt_name, "lavgand") == 0) {
817 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
818 Handle_err;
819 if (i == 0)
820 set_lor(cl->cl_option);
821 else
822 set_land(cl->cl_option);
823 if (debug_opt)
824 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
825 }
826
827 else if (strcmp(opt_name, "lavgor") == 0) {
828 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
829 Handle_err;
830 if (i == 0)
831 set_land(cl->cl_option);
832 else
833 set_lor(cl->cl_option);
834 if (debug_opt)
835 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
836 }
837
838 else if (strcmp(opt_name, "u") == 0 || strcmp(opt_name, "until") == 0) {
839 if (!in_brackets
840 || (ptr = get_time(ptr, &(cl->cl_until), 0)) == NULL)
841 Handle_err;
842 if (debug_opt)
843 fprintf(stderr, " Opt : \"%s\" %ld\n", opt_name,
844 (long int)cl->cl_until);
845 }
846
847 else if (strcmp(opt_name, "m") == 0 || strcmp(opt_name, "mail") == 0) {
848 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
849 Handle_err;
850 if (i == 0) {
851 clear_mail(cl->cl_option);
852 clear_mailzerolength(cl->cl_option);
853 }
854 else
855 set_mail(cl->cl_option);
856 if (debug_opt)
857 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
858 }
859
860 else if (strcmp(opt_name, "forcemail") == 0) {
861 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
862 Handle_err;
863 if (i == 0)
864 clear_mailzerolength(cl->cl_option);
865 else {
866 set_mailzerolength(cl->cl_option);
867 set_mail(cl->cl_option);
868 }
869 if (debug_opt)
870 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
871 }
872
873 else if (strcmp(opt_name, "mailfrom") == 0) {
874 int len = -1;
875
876 if (!in_brackets) {
877 Handle_err;
878 }
879
880 /* Also please note that we check if the mailfrom is valid in conf.c */
881 len = assign_option_string(&(cl->cl_mailfrom), ptr);
882 if (len < 0) {
883 Handle_err;
884 }
885 else {
886 ptr += len;
887 }
888
889 if (debug_opt)
890 fprintf(stderr, " Opt : \"%s\" \"%s\"\n", opt_name,
891 cl->cl_mailfrom);
892 }
893
894 else if (strcmp(opt_name, "mailto") == 0) {
895 int len = -1;
896
897 if (!in_brackets) {
898 Handle_err;
899 }
900
901 /* assign_option_string() set the value to NULL if the length is zero.
902 * However cl_mailto must not be NULL (as expected in
903 * conf.c:add_line_to_file()), so we check if the length is >= 0
904 * before calling assign_option_string() */
905 /* Also please note that we check if the mailto is valid in conf.c */
906 len = option_strlen(ptr);
907 if (len <= 0) {
908 clear_mail(cl->cl_option);
909 clear_mailzerolength(cl->cl_option);
910 if (debug_opt) {
911 fprintf(stderr, " Opt : \"mail\" 0\n");
912 fprintf(stderr, " Opt : \"forcemail\" 0\n");
913 }
914 }
915 else {
916 len = assign_option_string(&(cl->cl_mailto), ptr);
917 if (len < 0) {
918 Handle_err;
919 }
920 else {
921 ptr += len;
922 set_mail(cl->cl_option);
923 }
924 }
925
926 if (debug_opt)
927 fprintf(stderr, " Opt : \"%s\" \"%s\"\n", opt_name,
928 cl->cl_mailto);
929 }
930
931 else if (strcmp(opt_name, "erroronlymail") == 0) {
932 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
933 Handle_err;
934 if (i == 0)
935 clear_erroronlymail(cl->cl_option);
936 else
937 set_erroronlymail(cl->cl_option);
938 if (debug_opt)
939 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
940 }
941
942 else if (strcmp(opt_name, "dayand") == 0) {
943 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
944 Handle_err;
945 if (i == 0)
946 set_dayor(cl->cl_option);
947 else
948 set_dayand(cl->cl_option);
949 if (debug_opt)
950 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
951 }
952
953 else if (strcmp(opt_name, "dayor") == 0) {
954 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
955 Handle_err;
956 if (i == 0)
957 set_dayand(cl->cl_option);
958 else
959 set_dayor(cl->cl_option);
960 if (debug_opt)
961 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
962 }
963
964 else if (strcmp(opt_name, "nolog") == 0) {
965 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
966 Handle_err;
967 if (i == 0)
968 clear_nolog(cl->cl_option);
969 else
970 set_nolog(cl->cl_option);
971 if (debug_opt)
972 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
973 }
974
975 else if (strcmp(opt_name, "volatile") == 0) {
976 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
977 Handle_err;
978 if (i == 0)
979 clear_volatile(cl->cl_option);
980 else
981 set_volatile(cl->cl_option);
982 if (debug_opt)
983 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
984 }
985
986 else if (strcmp(opt_name, "stdout") == 0) {
987 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
988 Handle_err;
989 if (i == 0)
990 clear_stdout(cl->cl_option);
991 else
992 set_stdout(cl->cl_option);
993 if (debug_opt)
994 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
995 }
996
997 else if (strcmp(opt_name, "n") == 0 || strcmp(opt_name, "nice") == 0) {
998 if (!in_brackets || (ptr = get_nice(ptr, &i)) == NULL)
999 Handle_err;
1000 cl->cl_nice = (char)i;
1001 if (debug_opt)
1002 fprintf(stderr, " Opt : \"%s\" (-)%d\n", opt_name, i);
1003 }
1004
1005 else if (strcmp(opt_name, "runas") == 0) {
1006 int len = -1;
1007 char *runas = NULL;
1008 struct passwd *pas;
1009
1010 if (!in_brackets) {
1011 Handle_err;
1012 }
1013
1014 len = assign_option_string(&runas, ptr);
1015 if (len <= 0) {
1016 Handle_err;
1017 }
1018 else {
1019 ptr += len;
1020 }
1021
1022 if (getuid() != rootuid) {
1023 fprintf(stderr, "must be privileged to use option runas: "
1024 "skipping option\n");
1025 need_correction = 1;
1026 }
1027 else if (len > USER_NAME_LEN) {
1028 fprintf(stderr, "runas: user name \"%s\" longer than %d"
1029 "characters: skipping option\n", runas, USER_NAME_LEN);
1030 need_correction = 1;
1031 }
1032 else if ((pas = getpwnam(runas)) == NULL) {
1033 fprintf(stderr, "runas: \"%s\" is not in passwd file : "
1034 "ignored", runas);
1035 need_correction = 1;
1036 }
1037
1038 if (need_correction) {
1039 Free_safe(runas);
1040 }
1041 else {
1042 /* only set cl_runas if all is well, as cl_runas MUST always
1043 * be set to a valid value */
1044 Set(cl->cl_runas, runas);
1045 }
1046
1047 /* all good */
1048 if (debug_opt)
1049 fprintf(stderr, " Opt : \"%s\" %s ptr=%p\n", opt_name,
1050 cl->cl_runas, cl->cl_runas);
1051
1052 }
1053
1054 else if (strcmp(opt_name, "random") == 0) {
1055 if (in_brackets && (ptr = get_bool(ptr, &i)) == NULL)
1056 Handle_err;
1057 if (i == 0)
1058 clear_random(cl->cl_option);
1059 else
1060 set_random(cl->cl_option);
1061 if (debug_opt)
1062 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
1063 }
1064
1065 else if (strcmp(opt_name, "jitter") == 0) {
1066 if (!in_brackets
1067 || (ptr = get_num(ptr, &i, UCHAR_MAX, 0, NULL)) == NULL)
1068 Handle_err;
1069 cl->cl_jitter = i;
1070 if (debug_opt)
1071 fprintf(stderr, " Opt : \"%s\" %d\n", opt_name, i);
1072 }
1073
1074 /* handle %-line : we check if we are really in a %-line (which we do not do
1075 * for other options), because writing "&hourly" in a fcrontab results in an
1076 * error (hourly ignored) hard to find, and, in any case, annoying. */
1077 else if (cl->cl_runfreq == 1) {
1078 /* options to run once per interval :
1079 * ignore every fields below the limit */
1080 if (strcmp(opt_name, "mins") == 0) {
1081 /* nothing to do */
1082 if (debug_opt)
1083 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1084 }
1085 else if (strcmp(opt_name, "hours") == 0) {
1086 set_freq_mins(cl->cl_option);
1087 if (debug_opt)
1088 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1089 }
1090 else if (strcmp(opt_name, "days") == 0) {
1091 set_freq_mins(cl->cl_option);
1092 set_freq_hrs(cl->cl_option);
1093 if (debug_opt)
1094 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1095 }
1096 else if (strcmp(opt_name, "mons") == 0) {
1097 set_freq_mins(cl->cl_option);
1098 set_freq_hrs(cl->cl_option);
1099 set_freq_days(cl->cl_option);
1100 if (debug_opt)
1101 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1102 }
1103 else if (strcmp(opt_name, "dow") == 0) {
1104 set_freq_mins(cl->cl_option);
1105 set_freq_hrs(cl->cl_option);
1106 set_freq_days(cl->cl_option);
1107 set_freq_mons(cl->cl_option);
1108 if (debug_opt)
1109 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1110 }
1111
1112 /* run once an element of the selected field
1113 * (once an hour, once a day, etc) */
1114 else if (strcmp(opt_name, "hourly") == 0) {
1115 set_freq_hrs(cl->cl_option);
1116 set_freq_periodically(cl->cl_option);
1117 if (debug_opt)
1118 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1119 }
1120 else if (strcmp(opt_name, "daily") == 0) {
1121 set_freq_days(cl->cl_option);
1122 set_freq_periodically(cl->cl_option);
1123 if (debug_opt)
1124 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1125 }
1126 else if (strcmp(opt_name, "monthly") == 0) {
1127 set_freq_mons(cl->cl_option);
1128 set_freq_periodically(cl->cl_option);
1129 if (debug_opt)
1130 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1131 }
1132 else if (strcmp(opt_name, "weekly") == 0) {
1133 set_freq_dow(cl->cl_option);
1134 set_freq_periodically(cl->cl_option);
1135 if (debug_opt)
1136 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1137 }
1138
1139 /* run once an element of the selected field
1140 * from middle to middle of that field
1141 * (ie once from 12h to 12h the following day) */
1142 else if (strcmp(opt_name, "midhourly") == 0) {
1143 set_freq_hrs(cl->cl_option);
1144 set_freq_periodically(cl->cl_option);
1145 set_freq_mid(cl->cl_option);
1146 if (debug_opt)
1147 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1148 }
1149 else if (strcmp(opt_name, "middaily") == 0
1150 || strcmp(opt_name, "nightly") == 0) {
1151 set_freq_days(cl->cl_option);
1152 set_freq_periodically(cl->cl_option);
1153 set_freq_mid(cl->cl_option);
1154 if (debug_opt)
1155 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1156 }
1157 else if (strcmp(opt_name, "midmonthly") == 0) {
1158 set_freq_mons(cl->cl_option);
1159 set_freq_periodically(cl->cl_option);
1160 set_freq_mid(cl->cl_option);
1161 if (debug_opt)
1162 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1163 }
1164 else if (strcmp(opt_name, "midweekly") == 0) {
1165 set_freq_dow(cl->cl_option);
1166 set_freq_periodically(cl->cl_option);
1167 set_freq_mid(cl->cl_option);
1168 if (debug_opt)
1169 fprintf(stderr, " Opt : \"%s\"\n", opt_name);
1170 }
1171 }
1172
1173 else {
1174 fprintf(stderr, "%s:%d: Option \"%s\" unknown: "
1175 "skipping option.\n", file_name, line, opt_name);
1176 need_correction = 1;
1177 }
1178
1179 if (in_brackets) {
1180 if (*ptr != ')') {
1181 Handle_err}
1182 else
1183 ptr++;
1184 }
1185
1186 } while (*ptr == ',' && ptr++);
1187
1188 Skip_blanks(ptr);
1189 return ptr;
1190 }
1191
1192
1193 char *
get_time(char * ptr,time_t * time,int zero_allowed)1194 get_time(char *ptr, time_t * time, int zero_allowed)
1195 /* convert time read in string in time_t format */
1196 {
1197 time_t sum;
1198
1199 *time = 0;
1200
1201 while ((*ptr != ' ') && (*ptr != '\t') && (*ptr != '\0') && (*ptr != ')')) {
1202
1203 sum = 0;
1204
1205 while (isdigit((int)*ptr)) {
1206 sum *= 10;
1207 sum += *ptr - 48;
1208 ptr++;
1209 }
1210
1211 /* interpret multipliers */
1212 switch (*ptr) {
1213 case 'm': /* months */
1214 sum *= 4;
1215 case 'w': /* weeks */
1216 sum *= 7;
1217 case 'd': /* days */
1218 sum *= 24;
1219 case 'h': /* hours */
1220 sum *= 3600;
1221 case 's': /* seconds */
1222 ptr++;
1223 break;
1224 case ' ':
1225 case '\t':
1226 case ')':
1227 sum *= 60; /* minutes */
1228 break;
1229 default:
1230 need_correction = 1;
1231 return NULL;
1232 }
1233
1234 *time += sum;
1235
1236 }
1237
1238 Skip_blanks(ptr);
1239 if (*time == 0 && !zero_allowed) {
1240 need_correction = 1;
1241 return NULL;
1242 }
1243 else
1244 return ptr;
1245 }
1246
1247
1248 char *
check_username(char * ptr,cf_t * cf,cl_t * cl)1249 check_username(char *ptr, cf_t * cf, cl_t * cl)
1250 /* check ptr to see if the first word is a username, returns new ptr */
1251 {
1252 short int indx = 0;
1253 char username[USER_NAME_LEN];
1254 struct passwd *userpwent;
1255
1256 /* check to see if next word is a username */
1257 /* we don't allow quotes, to be able to distinguish a user name from
1258 * a command line (where quotes are allowed) */
1259 while (isalnum((int)ptr[indx]) || ptr[indx] == '-' || ptr[indx] == '_')
1260 indx++;
1261 if (indx >= USER_NAME_LEN)
1262 indx = USER_NAME_LEN - 1;
1263 strncpy(username, ptr, indx);
1264 username[indx] = '\0';
1265
1266 if ((userpwent = getpwnam(username)) != NULL) {
1267 /* found the user */
1268 ptr = ptr + indx; /* move ptr to the next word */
1269 Skip_blanks(ptr);
1270
1271 if (getuid() != rootuid) {
1272 fprintf(stderr, "must be privileged to run as another user : "
1273 "ignoring\n");
1274 }
1275 else {
1276 Set(cl->cl_runas, username);
1277 if (debug_opt)
1278 fprintf(stderr, " Opt : inline_runas %s\n", username);
1279 }
1280 }
1281
1282 return ptr;
1283 }
1284
1285
1286 char *
read_word(char * ptr,char * buf,size_t buf_size)1287 read_word(char *ptr, char *buf, size_t buf_size)
1288 /* copy a word to buf with a max_size of buf_size and return a pointer
1289 * to the next char after the word */
1290 {
1291 int i = 0;
1292
1293 bzero(buf, buf_size);
1294
1295 while (isalnum((int)*ptr) && i < buf_size)
1296 buf[i++] = *ptr++;
1297
1298 return ptr;
1299 }
1300
1301 int
read_shortcut(char * ptr,cf_t * cf)1302 read_shortcut(char *ptr, cf_t * cf)
1303 /* try to read a shortcut entry, and if it is one then append a line to cf
1304 * Return 1 if that was a shortcut entry. If it wasn't, return 0 and make sure
1305 * ptr is back to its orig value. */
1306 {
1307 cl_t *cl = NULL;
1308 char shortcut[20];
1309 char *ptr_orig = ptr;
1310
1311 cl = dups_cl(&default_line);
1312
1313 /* skip the @ */
1314 ptr++;
1315
1316 ptr = read_word(ptr, shortcut, sizeof(shortcut));
1317
1318 /* time&date by default -- we'll switch to freq if @reboot */
1319 set_td(cl->cl_option);
1320 cl->cl_remain = cl->cl_runfreq; /* FIXME: necessary?? */
1321
1322 if (strcmp(shortcut, "reboot") == 0) {
1323 set_freq(cl->cl_option);
1324 set_runatreboot(cl->cl_option);
1325 set_runonce(cl->cl_option);
1326 clear_volatile(cl->cl_option);
1327 cl->cl_runfreq = 0;
1328 cl->cl_first = 0;
1329 /* the job will not be rescheduled after the first execution (flag is_hasrun),
1330 * we set timefreq to LONG_MAX just in case */
1331 cl->cl_timefreq = LONG_MAX;
1332
1333 if (debug_opt)
1334 fprintf(stderr, " Shc : @reboot\n");
1335 }
1336 else if (strcmp(shortcut, "yearly") == 0
1337 || strcmp(shortcut, "annually") == 0) {
1338 bit_set(cl->cl_mins, 0);
1339 bit_set(cl->cl_hrs, 0);
1340 bit_set(cl->cl_days, 1);
1341 bit_set(cl->cl_mons, 0);
1342 bit_nset(cl->cl_dow, 0, 7);
1343
1344 if (debug_opt)
1345 fprintf(stderr, " Shc : @yearly\n");
1346 }
1347 else if (strcmp(shortcut, "monthly") == 0) {
1348 bit_set(cl->cl_mins, 0);
1349 bit_set(cl->cl_hrs, 0);
1350 bit_set(cl->cl_days, 1);
1351 bit_nset(cl->cl_mons, 0, 11);
1352 bit_nset(cl->cl_dow, 0, 7);
1353
1354 if (debug_opt)
1355 fprintf(stderr, " Shc : @monthly\n");
1356 }
1357 else if (strcmp(shortcut, "weekly") == 0) {
1358 bit_set(cl->cl_mins, 0);
1359 bit_set(cl->cl_hrs, 0);
1360 bit_nset(cl->cl_days, 0, 31);
1361 bit_nset(cl->cl_mons, 0, 11);
1362 bit_set(cl->cl_dow, 0);
1363 bit_set(cl->cl_dow, 7); /* 0 and 7 are both sunday */
1364
1365 if (debug_opt)
1366 fprintf(stderr, " Shc : @weekly\n");
1367 }
1368 else if (strcmp(shortcut, "daily") == 0
1369 || strcmp(shortcut, "midnight") == 0) {
1370 bit_set(cl->cl_mins, 0);
1371 bit_set(cl->cl_hrs, 0);
1372 bit_nset(cl->cl_days, 0, 31);
1373 bit_nset(cl->cl_mons, 0, 11);
1374 bit_nset(cl->cl_dow, 0, 7);
1375
1376 if (debug_opt)
1377 fprintf(stderr, " Shc : @daily\n");
1378 }
1379 else if (strcmp(shortcut, "hourly") == 0) {
1380 bit_set(cl->cl_mins, 0);
1381 bit_nset(cl->cl_hrs, 0, 23);
1382 bit_nset(cl->cl_days, 0, 31);
1383 bit_nset(cl->cl_mons, 0, 11);
1384 bit_nset(cl->cl_dow, 0, 7);
1385
1386 if (debug_opt)
1387 fprintf(stderr, " Shc : @hourly\n");
1388 }
1389 else {
1390 /* this is not a shortcut line but a normal @-line: */
1391 ptr = ptr_orig;
1392 return 0;
1393 }
1394
1395 /* The next char must be a space (no other option allowed when using
1396 * a shortcut: if the user wants to use options, they should use the
1397 * native fcron lines */
1398 if (!isspace((int)*ptr)) {
1399 fprintf(stderr, "%s:%d: No space after shortcut: skipping line.\n",
1400 file_name, line);
1401 goto exiterr;
1402 }
1403
1404 /* skip spaces before the username / shell command */
1405 while (isspace((int)*ptr))
1406 ptr++;
1407
1408 /* check for inline runas */
1409 ptr = check_username(ptr, cf, cl);
1410
1411 /* get cl_shell field ( remove trailing blanks ) */
1412 if ((cl->cl_shell = get_string(ptr)) == NULL) {
1413 fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1414 file_name, line);
1415 goto exiterr;
1416 }
1417 if (strcmp(cl->cl_shell, "\0") == 0) {
1418 fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1419 file_name, line);
1420 Free_safe(cl->cl_shell);
1421 goto exiterr;
1422 }
1423
1424 #ifndef USE_SENDMAIL
1425 clear_mail(cl->cl_option);
1426 clear_mailzerolength(cl->cl_option);
1427 #endif
1428
1429 cl->cl_next = cf->cf_line_base;
1430 cf->cf_line_base = cl;
1431
1432 if (debug_opt)
1433 fprintf(stderr, " Cmd \"%s\" (shortcut)\n", cl->cl_shell);
1434 return 1;
1435
1436 exiterr:
1437 Free_safe(cl);
1438 need_correction = 1;
1439 return 1;
1440 }
1441
1442 void
read_freq(char * ptr,cf_t * cf)1443 read_freq(char *ptr, cf_t * cf)
1444 /* read a freq entry, and append a line to cf */
1445 {
1446 cl_t *cl = NULL;
1447
1448 cl = dups_cl(&default_line);
1449
1450 cl->cl_first = -1; /* 0 is a valid value, so we have to use -1 to detect unset */
1451
1452 /* skip the @ */
1453 ptr++;
1454
1455 /* get the time before first execution or the options */
1456 if (isdigit((int)*ptr)) {
1457 if ((ptr = get_time(ptr, &(cl->cl_first), 1)) == NULL) {
1458 fprintf(stderr, "%s:%d: Error while reading first delay:"
1459 " skipping line.\n", file_name, line);
1460 goto exiterr;
1461 }
1462
1463 Skip_blanks(ptr);
1464 }
1465 else if (isalnum((int)*ptr)) {
1466 if ((ptr = read_opt(ptr, cl)) == NULL)
1467 goto exiterr;
1468 }
1469 else
1470 Skip_blanks(ptr);
1471
1472 /* we set this here, because it may be unset by read_opt (reset option) */
1473 cl->cl_runfreq = 0;
1474 set_freq(cl->cl_option);
1475
1476 /* then cl_timefreq */
1477 if ((ptr = get_time(ptr, (time_t *) & (cl->cl_timefreq), 0)) == NULL
1478 || cl->cl_timefreq < 1) {
1479 fprintf(stderr,
1480 "%s:%d: Error while reading frequency %s: skipping line.\n",
1481 file_name, line,
1482 (cl->cl_timefreq < 1) ? "(lower than 1s) " : "");
1483 goto exiterr;
1484 }
1485
1486 if (cl->cl_timefreq == 0) {
1487 fprintf(stderr, "%s:%d: no freq specified: skipping line.\n",
1488 file_name, line);
1489 goto exiterr;
1490 }
1491
1492 if (cl->cl_first == -1)
1493 /* time before first execution was not specified explicitely */
1494 cl->cl_first = cl->cl_timefreq;
1495
1496 /* check for inline runas */
1497 ptr = check_username(ptr, cf, cl);
1498
1499 /* get cl_shell field ( remove trailing blanks ) */
1500 if ((cl->cl_shell = get_string(ptr)) == NULL) {
1501 fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1502 file_name, line);
1503 goto exiterr;
1504 }
1505 if (strcmp(cl->cl_shell, "\0") == 0) {
1506 fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1507 file_name, line);
1508 Free_safe(cl->cl_shell);
1509 goto exiterr;
1510 }
1511
1512 #ifndef USE_SENDMAIL
1513 clear_mail(cl->cl_option);
1514 clear_mailzerolength(cl->cl_option);
1515 #endif
1516
1517 cl->cl_next = cf->cf_line_base;
1518 cf->cf_line_base = cl;
1519
1520 if (debug_opt)
1521 fprintf(stderr, " Cmd \"%s\", timefreq %ld, first %ld\n",
1522 cl->cl_shell, cl->cl_timefreq, (long int)cl->cl_first);
1523
1524 return;
1525
1526 exiterr:
1527 free_line(cl);
1528 need_correction = 1;
1529 return;
1530 }
1531
1532
1533
1534 #define R_field(PTR, ARY, MAX, ARYCONST, DESCRP) \
1535 if((PTR = read_field(PTR, ARY, MAX, ARYCONST)) == NULL) { \
1536 if (debug_opt) \
1537 fprintf(stderr, "\n"); \
1538 fprintf(stderr, "%s:%d: Error while reading " DESCRP " field: " \
1539 "skipping line.\n", file_name, line); \
1540 Free_safe(cl); \
1541 return; \
1542 }
1543
1544
1545 void
read_arys(char * ptr,cf_t * cf)1546 read_arys(char *ptr, cf_t * cf)
1547 /* read a run freq number plus a normal fcron line */
1548 {
1549 cl_t *cl = NULL;
1550 int i = 0;
1551
1552 cl = dups_cl(&default_line);
1553
1554 /* set cl_remain if not specified */
1555 if (*ptr == '&') {
1556 ptr++;
1557 if (isdigit((int)*ptr)) {
1558 if ((ptr = get_num(ptr, &i, USHRT_MAX, 0, NULL)) == NULL) {
1559 fprintf(stderr, "%s:%d: Error while reading runfreq:"
1560 " skipping line.\n", file_name, line);
1561 goto exiterr;
1562 }
1563 else {
1564 if (i <= 1) {
1565 fprintf(stderr, "%s:%d: runfreq must be 2 or more :"
1566 " skipping line.\n", file_name, line);
1567 goto exiterr;
1568 }
1569 cl->cl_runfreq = (unsigned short)i;
1570 }
1571 }
1572 else if (isalnum((int)*ptr))
1573 if ((ptr = read_opt(ptr, cl)) == NULL) {
1574 goto exiterr;
1575 }
1576 Skip_blanks(ptr);
1577 }
1578
1579 cl->cl_remain = cl->cl_runfreq;
1580
1581 /* we set this here, because it may be unset by read_opt (reset option) */
1582 set_td(cl->cl_option);
1583
1584 if (debug_opt)
1585 fprintf(stderr, " ");
1586
1587 /* get the fields (check for errors) */
1588 R_field(ptr, cl->cl_mins, 59, NULL, "minutes");
1589 R_field(ptr, cl->cl_hrs, 23, NULL, "hours");
1590 R_field(ptr, cl->cl_days, 31, NULL, "days");
1591 /* month are defined by user from 1 to 12 : max is 12 */
1592 R_field(ptr, cl->cl_mons, 12, mons_ary, "months");
1593 R_field(ptr, cl->cl_dow, 7, dows_ary, "days of week");
1594
1595 if (debug_opt)
1596 /* if debug_opt is set, we print informations in read_field function,
1597 * but no end line : we print it here */
1598 fprintf(stderr, " remain %d\n", cl->cl_remain);
1599
1600 /* check for inline runas */
1601 ptr = check_username(ptr, cf, cl);
1602
1603 /* get the shell command (remove trailing blanks) */
1604 if ((cl->cl_shell = get_string(ptr)) == NULL) {
1605 fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1606 file_name, line);
1607 goto exiterr;
1608 }
1609 if (strcmp(cl->cl_shell, "\0") == 0) {
1610 fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1611 file_name, line);
1612 Free_safe(cl->cl_shell);
1613 goto exiterr;
1614 }
1615
1616 #ifndef USE_SENDMAIL
1617 clear_mail(cl->cl_option);
1618 clear_mailzerolength(cl->cl_option);
1619 #endif
1620
1621 cl->cl_next = cf->cf_line_base;
1622 cf->cf_line_base = cl;
1623
1624 if (debug_opt)
1625 fprintf(stderr, " Cmd \"%s\"\n", cl->cl_shell);
1626 return;
1627
1628 exiterr:
1629 need_correction = 1;
1630 free_line(cl);
1631 return;
1632
1633 }
1634
1635 void
read_period(char * ptr,cf_t * cf)1636 read_period(char *ptr, cf_t * cf)
1637 /* read a line to run periodically (i.e. once a day, once a week, etc) */
1638 {
1639 cl_t *cl = NULL;
1640 short int remain = 8;
1641
1642 cl = dups_cl(&default_line);
1643
1644 /* skip the % */
1645 ptr++;
1646
1647 /* a runfreq set to 1 means : this is a periodical line
1648 * (runfreq cannot be changed by read_opt() if already set to 1) */
1649 cl->cl_remain = cl->cl_runfreq = 1;
1650
1651 /* set cl_remain if not specified */
1652 if ((ptr = read_opt(ptr, cl)) == NULL) {
1653 goto exiterr;
1654 }
1655 Skip_blanks(ptr);
1656
1657 /* we set this here, because it may be unset by read_opt (reset option) */
1658 set_td(cl->cl_option);
1659
1660 if (debug_opt)
1661 fprintf(stderr, " ");
1662
1663 if (is_freq_periodically(cl->cl_option)) {
1664 if (is_freq_mins(cl->cl_option))
1665 remain = 0;
1666 else if (is_freq_hrs(cl->cl_option))
1667 remain = 1;
1668 else if (is_freq_days(cl->cl_option))
1669 remain = 2;
1670 else if (is_freq_mons(cl->cl_option))
1671 remain = 3;
1672 else if (is_freq_dow(cl->cl_option))
1673 remain = 2;
1674 }
1675
1676 /* get the fields (check for errors) */
1677 if (remain-- > 0) {
1678 R_field(ptr, cl->cl_mins, 59, NULL, "minutes")}
1679 else
1680 bit_nset(cl->cl_mins, 0, 59);
1681 if (remain-- > 0) {
1682 R_field(ptr, cl->cl_hrs, 23, NULL, "hours")}
1683 else
1684 bit_nset(cl->cl_hrs, 0, 23);
1685 if (remain-- > 0) {
1686 R_field(ptr, cl->cl_days, 31, NULL, "days")}
1687 else
1688 bit_nset(cl->cl_days, 1, 32);
1689 /* month are defined by user from 1 to 12 : max is 12 */
1690 if (remain-- > 0) {
1691 R_field(ptr, cl->cl_mons, 12, mons_ary, "months")}
1692 else
1693 bit_nset(cl->cl_mons, 0, 11);
1694 if (remain-- > 0) {
1695 R_field(ptr, cl->cl_dow, 7, dows_ary, "days of week")}
1696 else
1697 bit_nset(cl->cl_dow, 0, 7);
1698
1699 if (debug_opt)
1700 /* if debug_opt is set, we print informations in read_field function,
1701 * but no end line : we print it here */
1702 fprintf(stderr, " remain %d\n", cl->cl_remain);
1703
1704 /* check for inline runas */
1705 ptr = check_username(ptr, cf, cl);
1706
1707 /* get the shell command (remove trailing blanks) */
1708 if ((cl->cl_shell = get_string(ptr)) == NULL) {
1709 fprintf(stderr, "%s:%d: Mismatched quotes: skipping line.\n",
1710 file_name, line);
1711 goto exiterr;
1712 }
1713 if (strcmp(cl->cl_shell, "\0") == 0) {
1714 fprintf(stderr, "%s:%d: No shell command: skipping line.\n",
1715 file_name, line);
1716 Free_safe(cl->cl_shell);
1717 goto exiterr;
1718 }
1719 else if (cl->cl_shell[0] == '*' || isdigit((int)cl->cl_shell[0]))
1720 fprintf(stderr, "%s:%d: Warning : shell command beginning by '%c'.\n",
1721 file_name, line, cl->cl_shell[0]);
1722
1723 /* check for non matching if option runfreq is set to 1 */
1724 if (!is_freq_periodically(cl->cl_option)) {
1725 const size_t s_mins = 60, s_hrs = 24;
1726 const size_t s_days = 32, s_mons = 12;
1727 const size_t s_dow = 8;
1728 int j = 0;
1729
1730 if (!is_freq_mins(cl->cl_option)) {
1731 bit_ffc(cl->cl_mins, s_mins, &j);
1732 if (j != -1 && j < s_mins)
1733 goto ok;
1734 }
1735 if (!is_freq_hrs(cl->cl_option)) {
1736 bit_ffc(cl->cl_hrs, s_hrs, &j);
1737 if (j != -1 && j < s_hrs)
1738 goto ok;
1739 }
1740 if (!is_freq_days(cl->cl_option)) {
1741 bit_ffc(cl->cl_days, s_days, &j);
1742 if (j != -1 && j < s_days) {
1743 if (is_dayand(cl->cl_option))
1744 goto ok;
1745 else {
1746 if (!is_freq_dow(cl->cl_option)) {
1747 bit_ffc(cl->cl_dow, s_dow, &j);
1748 if (j != -1 && j < s_dow)
1749 goto ok;
1750 }
1751 }
1752 }
1753 }
1754 if (!is_freq_mons(cl->cl_option)) {
1755 bit_ffc(cl->cl_mons, s_mons, &j);
1756 if (j != -1 && j < s_mons)
1757 goto ok;
1758 }
1759 if (!is_freq_dow(cl->cl_option)) {
1760 bit_ffc(cl->cl_dow, s_dow, &j);
1761 if (j != -1 && j < s_dow && is_dayand(cl->cl_option))
1762 goto ok;
1763 }
1764
1765 fprintf(stderr, "%s:%d: periodical line with no intervals: "
1766 "skipping line.\n", file_name, line);
1767 goto exiterr;
1768 }
1769
1770 ok:
1771 #ifndef USE_SENDMAIL
1772 clear_mail(cl->cl_option);
1773 clear_mailzerolength(cl->cl_option);
1774 #endif
1775
1776 cl->cl_next = cf->cf_line_base;
1777 cf->cf_line_base = cl;
1778
1779 if (debug_opt)
1780 fprintf(stderr, " Cmd \"%s\"\n", cl->cl_shell);
1781 return;
1782
1783 exiterr:
1784 need_correction = 1;
1785 free_line(cl);
1786 return;
1787
1788 }
1789
1790 char *
get_num(char * ptr,int * num,int max,short int decimal,const char ** names)1791 get_num(char *ptr, int *num, int max, short int decimal, const char **names)
1792 /* read a string's number and return it under int format.
1793 * Also check if that number is less than max */
1794 {
1795 int i = 0;
1796 *num = 0;
1797
1798 if (isalpha((int)*ptr)) {
1799
1800 if (names == NULL) {
1801 need_correction = 1;
1802 return NULL;
1803 }
1804
1805 /* set string to lower case */
1806 for (i = 0; i < strlen(names[0]); i++)
1807 *(ptr + i) = tolower(*(ptr + i));
1808
1809 for (i = 0; names[i]; ++i)
1810 if (strncmp(ptr, names[i], strlen(names[i])) == 0) {
1811 *num = i;
1812 ptr += strlen(names[i]);
1813 return ptr;
1814 break;
1815 }
1816
1817 /* string is not in name list */
1818 need_correction = 1;
1819 return NULL;
1820
1821 }
1822 else {
1823
1824 while (isdigit((int)*ptr) || *ptr == '.') {
1825
1826 if (*ptr == '.' && ptr++ && i++ > 0)
1827 return NULL;
1828 if (i > 0 && --decimal < 0) {
1829 /* the decimal number is exceeded : we round off,
1830 * skip the other decimals and return */
1831 if (*ptr >= '5')
1832 *num += 1;
1833 while (isdigit((int)*(++ptr))) ;
1834 ptr--;
1835 }
1836 else {
1837 *num *= 10;
1838 *num += *ptr - 48;
1839 }
1840
1841 if (*num > max) {
1842 need_correction = 1;
1843 return NULL;
1844 }
1845
1846 ptr++;
1847
1848 }
1849
1850 if (decimal > 0)
1851 *num *= 10 * decimal;
1852
1853 }
1854
1855 return ptr;
1856 }
1857
1858
1859 char *
read_field(char * ptr,bitstr_t * ary,int max,const char ** names)1860 read_field(char *ptr, bitstr_t * ary, int max, const char **names)
1861 /* read a field like "2,5-8,10-20/2,21-30~25" and fill ary */
1862 {
1863 int start = 0;
1864 int stop = 0;
1865 int step = 0;
1866 int rm = 0;
1867 int i = 0;
1868
1869 while ((*ptr != ' ') && (*ptr != '\t') && (*ptr != '\0')) {
1870
1871 start = stop = step = 0;
1872
1873 /* there may be a "," */
1874 if (*ptr == ',')
1875 ptr++;
1876
1877 if (*ptr == '*') {
1878 /* we have to fill everything (may be modified by a step ) */
1879 start = 0;
1880 /* user set month from 1 to 12, but we manage it internally
1881 * as a number from 0 to 11 */
1882 stop = (max == 12) ? 11 : max;
1883 ptr++;
1884 }
1885 else {
1886
1887 ptr = get_num(ptr, &start, max, 0, names);
1888 if (ptr == NULL)
1889 return NULL;
1890 if (max == 12)
1891 /* this number is part of the month field.
1892 * user set it from 1 to 12, but we manage it internally
1893 * as a number from 0 to 11 : we remove 1 to start */
1894 start -= 1;
1895
1896 if (*ptr == ',' || *ptr == ' ' || *ptr == '\t') {
1897 /* this is a single number : set up array and continue */
1898 if (debug_opt)
1899 fprintf(stderr, " %d", start);
1900 bit_set(ary, start);
1901 continue;
1902 }
1903
1904 /* check for a dash */
1905 else if (*ptr == '-') {
1906 ptr++;
1907 ptr = get_num(ptr, &stop, max, 0, names);
1908 if (ptr == NULL)
1909 /* we reached the end of the string to parse */
1910 return NULL;
1911 if (max == 12)
1912 /* this number is part of the month field.
1913 * user set it from 1 to 12, but we manage it internally
1914 * as a number from 0 to 11 : we remove 1 to stop */
1915 stop -= 1;
1916 }
1917 else {
1918 /* syntax error */
1919 need_correction = 1;
1920 return NULL;
1921 }
1922 }
1923
1924 /* check for step size */
1925 if (*ptr == '/') {
1926 ptr++;
1927 if ((ptr = get_num(ptr, &step, max, 0, names)) == NULL || step == 0)
1928 return NULL;
1929 }
1930 else
1931 /* step undefined : default is 0 */
1932 step = 1;
1933
1934 /* fill array */
1935 if (debug_opt)
1936 fprintf(stderr, " %d-%d/%d", start, stop, step);
1937
1938 if (start < stop)
1939 for (i = start; i <= stop; i += step)
1940 bit_set(ary, i);
1941 else {
1942 short int field_max = (max == 12) ? 11 : max;
1943 /* this is a field like (for hours field) "22-3" :
1944 * we should set from 22 to 3 (not from 3 to 22 or nothing :)) ) */
1945 for (i = start; i <= field_max; i += step)
1946 bit_set(ary, i);
1947 for (i -= (field_max + 1); i <= stop; i += step)
1948 bit_set(ary, i);
1949 }
1950
1951 /* finally, remove unwanted values */
1952 while (*ptr == '~') {
1953 ptr++;
1954 rm = 0;
1955 if ((ptr = get_num(ptr, &rm, max, 0, names)) == NULL)
1956 return NULL;
1957 if (max == 12)
1958 /* this number is part of the month field.
1959 * user set it from 1 to 12, but we manage it internally
1960 * as a number from 0 to 11 : we remove 1 to rm */
1961 rm -= 1;
1962
1963 if (debug_opt)
1964 fprintf(stderr, " ~%d", rm);
1965 bit_clear(ary, rm);
1966
1967 /* if we remove one value of Sunday, remove the other */
1968 if (max == 7 && rm == 0) {
1969 bit_clear(ary, 7);
1970 if (debug_opt)
1971 fprintf(stderr, " ~%d", 7);
1972 }
1973 else if (max == 7 && rm == 7) {
1974 bit_clear(ary, 0);
1975 if (debug_opt)
1976 fprintf(stderr, " ~%d", 0);
1977 }
1978
1979 }
1980
1981 }
1982
1983 /* Sunday is both 0 and 7 : if one is set, set the other */
1984 if (max == 7) {
1985 if (bit_test(ary, 0))
1986 bit_set(ary, 7);
1987 else if (bit_test(ary, 7))
1988 bit_set(ary, 0);
1989 }
1990
1991 Skip_blanks(ptr);
1992
1993 if (debug_opt)
1994 fprintf(stderr, " #");
1995
1996 return ptr;
1997 }
1998
1999 void
delete_file(const char * user_name)2000 delete_file(const char *user_name)
2001 /* free a file if user_name is not null
2002 * otherwise free all files */
2003 {
2004 cf_t *file = NULL;
2005 cf_t *prev_file = NULL;
2006 cl_t *line = NULL;
2007 cl_t *cur_line = NULL;
2008
2009 file = file_base;
2010 while (file != NULL) {
2011 if (strcmp(user_name, file->cf_user) == 0) {
2012
2013 /* free lines */
2014 cur_line = file->cf_line_base;
2015 while ((line = cur_line) != NULL) {
2016 cur_line = line->cl_next;
2017 free_line(line);
2018 }
2019 break;
2020
2021 }
2022 else {
2023 prev_file = file;
2024 file = file->cf_next;
2025 }
2026 }
2027
2028 if (file == NULL)
2029 /* file not in list */
2030 return;
2031
2032 /* remove file from list */
2033 if (prev_file == NULL)
2034 file_base = file->cf_next;
2035 else
2036 prev_file->cf_next = file->cf_next;
2037
2038 /* free env variables */
2039 env_list_destroy(file->cf_env_list);
2040
2041 /* finally free file itself */
2042 Free_safe(file->cf_user);
2043 Free_safe(file);
2044
2045 }
2046
2047 int
save_file(char * path)2048 save_file(char *path)
2049 /* Store the informations relatives to the executions
2050 * of tasks at a defined frequency of system's running time */
2051 {
2052 cf_t *file = NULL;
2053
2054 if (debug_opt)
2055 fprintf(stderr, "Saving ...\n");
2056
2057 for (file = file_base; file; file = file->cf_next) {
2058
2059 /* save_file() is run under user's rights.
2060 * If fcrontab is run by root for a normal user, we must change the file's
2061 * ownership to this user, in order to make fcron check the runas fields.
2062 * (a malicious user could put a runas(root) and wait for the fcrontab to be
2063 * installed by root) */
2064 #ifdef USE_SETE_ID
2065 if (save_file_safe(file, path, "fcrontab", asuid, fcrontab_gid, 0) ==
2066 ERR)
2067 return ERR;
2068 #else
2069 if (save_file_safe
2070 (file, path, "fcrontab", fcrontab_uid, fcrontab_gid, 0) == ERR)
2071 return ERR;
2072 #endif
2073
2074 }
2075
2076 return OK;
2077 }
2078