From 636f4217e3675521233328ef97e710c9096f6307 Mon Sep 17 00:00:00 2001 From: thib Date: Sat, 27 Jan 2001 15:41:09 +0000 Subject: [PATCH] added option strict and noticenotrun --- conf.c | 70 ++++++++++------- database.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++------ option.h | 19 +++-- 3 files changed, 253 insertions(+), 60 deletions(-) diff --git a/conf.c b/conf.c index ada26a1..4044921 100644 --- a/conf.c +++ b/conf.c @@ -22,7 +22,7 @@ * `LICENSE' that comes with the fcron source distribution. */ - /* $Id: conf.c,v 1.36 2001-01-12 21:43:41 thib Exp $ */ + /* $Id: conf.c,v 1.37 2001-01-27 15:41:09 thib Exp $ */ #include "fcron.h" @@ -495,11 +495,43 @@ read_file(const char *file_name, CF *cf) cl->cl_runas = runas; } + /* we need that here because the user's name (contained in the + * struct CF may be required */ + cl->cl_file = cf; + + /* check if the task has not been stopped during execution and + * insert in lavg or serial queues the jobs which was in one + * at fcron's stop and the bootrun jobs */ + if (cl->cl_numexe > 0) { + cl->cl_numexe = 0; + if ( ! is_strict(cl->cl_option) ) { + if ( is_lavg(cl->cl_option) ) + add_lavg_job(cl); + else if ( is_serial(cl->cl_option) + || is_serial_once(cl->cl_option) ) + add_serial_job(cl); + else { + /* job has been stopped during execution : + * launch it again */ + warn("job %s did not finish : running it again.", + cl->cl_shell); + set_serial_once(cl->cl_option); + add_serial_job(cl); + } + } + } + if ( is_td(cl->cl_option) ) { /* set the time and date of the next execution */ if ( cl->cl_nextexe <= now ) { - if ( is_bootrun(cl->cl_option) && t_save != 0) { + if ( cl->cl_nextexe == 0 ) + /* the is a line from a new file */ + set_next_exe(cl, NO_GOTO); + else if(cl->cl_runfreq == 1 && is_notice_notrun(cl->cl_option)) + set_next_exe_notrun(cl, SYSDOWN); + else if ( is_bootrun(cl->cl_option) && t_save != 0 + && cl->cl_runfreq != 1) { if ( cl->cl_remain > 0 && --cl->cl_remain > 0 ) { debug(" cl_remain: %d", cl->cl_remain); } @@ -510,10 +542,13 @@ read_file(const char *file_name, CF *cf) if ( ! is_lavg(cl->cl_option) ) set_serial_once(cl->cl_option); } - set_next_exe(cl, 0); + set_next_exe(cl, STD); + } + else { + if ( is_notice_notrun(cl->cl_option) ) + mail_notrun(cl, SYSDOWN, NULL); + set_next_exe(cl, NO_GOTO); } - else - set_next_exe(cl, 1); } else insert_nextexe(cl); @@ -526,25 +561,6 @@ read_file(const char *file_name, CF *cf) insert_nextexe(cl); } - /* check if the task has not been stopped during execution and - * insert in lavg or serial queues the jobs which was in one - * at fcron's stop and the bootrun jobs */ - if (cl->cl_numexe > 0) { - cl->cl_numexe = 0; - if ( is_lavg(cl->cl_option) ) - add_lavg_job(cl); - else if (is_serial(cl->cl_option) || is_serial_once(cl->cl_option)) - add_serial_job(cl); - else { - /* job has been stopped during execution : - * launch it again */ - warn("job %s did not finish : running it again.", - cl->cl_shell); - set_serial_once(cl->cl_option); - add_serial_job(cl); - } - } - if (debug_opt) { struct tm *ftime; ftime = localtime( &(cl->cl_nextexe) ); @@ -557,7 +573,6 @@ read_file(const char *file_name, CF *cf) cl->cl_next = cf->cf_line_base; cf->cf_line_base = cl; - cl->cl_file = cf; Alloc(cl, CL); } /* check for an error */ @@ -568,11 +583,6 @@ read_file(const char *file_name, CF *cf) /* free last calloc : unused */ free(cl); -/* // if (fgets(buf, sizeof(buf), ff) == NULL || */ -/* // strncmp(buf, "eof\n", sizeof("eof\n")) != 0) */ -/* // error("file %s is truncated : you should reinstall it with fcrontab", - file_name); */ - fclose(ff); return 0; diff --git a/database.c b/database.c index 0c24129..6786ad4 100644 --- a/database.c +++ b/database.c @@ -22,14 +22,14 @@ * `LICENSE' that comes with the fcron source distribution. */ - /* $Id: database.c,v 1.43 2001-01-12 21:44:45 thib Exp $ */ + /* $Id: database.c,v 1.44 2001-01-27 15:44:39 thib Exp $ */ #include "fcron.h" int is_leap_year(int year); int get_nb_mdays(int year, int mon); void set_wday(struct tm *date); -void goto_non_matching(CL *line, struct tm *tm); +void goto_non_matching(CL *line, struct tm *tm, char option); void run_normal_job(CL *line); void run_serial_job(void); void run_lavg_job(int i); @@ -37,7 +37,7 @@ void run_queue_job(CL *line); void -test_jobs(time_t t2) +test_jobs(void) /* determine which jobs need to be run, and run them. */ { struct job *j; @@ -46,9 +46,9 @@ test_jobs(time_t t2) debug("Looking for jobs to execute ..."); /* // */ - while ( (j=queue_base) && j->j_line->cl_nextexe <= t2 ){ - set_next_exe(j->j_line, 0); + while ( (j=queue_base) && j->j_line->cl_nextexe <= now ){ if ( j->j_line->cl_remain > 0 && --(j->j_line->cl_remain) > 0) { + set_next_exe(j->j_line, STD); debug(" cl_remain: %d", j->j_line->cl_remain); continue ; } @@ -61,6 +61,8 @@ test_jobs(time_t t2) add_serial_job(j->j_line); else run_normal_job(j->j_line); + + set_next_exe(j->j_line, STD); } } @@ -280,6 +282,8 @@ add_serial_job(CL *line) void add_lavg_job(CL *line) /* add the next queued job in lavg queue */ + /* WARNING : must be run before a set_next_exe() to get the strict option + * working correctly */ { /* check if the line is already in the lavg queue */ @@ -322,9 +326,35 @@ add_lavg_job(CL *line) lavg_array[lavg_num].l_line = line; line->cl_numexe += 1; - lavg_array[lavg_num++].l_until = - (line->cl_until > 0) ? now + line->cl_until : 0; + set_run_if_late(line->cl_option); + if ( is_strict(line->cl_option) && line->cl_runfreq == 1) { + struct tm *ft; + struct tm ftime; + time_t end_of_cur_int = 0; + + ft = localtime(&line->cl_nextexe); + + /* localtime() function seem to return every time the same pointer : + it resets our previous changes, so we need to prevent it + ( localtime() is used in the debug() function) */ + memcpy(&ftime, ft, sizeof(struct tm)); + + goto_non_matching(line, &ftime, END_OF_INTERVAL); + + end_of_cur_int = mktime(&ftime); + + if ((line->cl_until > 0) && (line->cl_until + now < end_of_cur_int)) + lavg_array[lavg_num].l_until = line->cl_until + now; + else { + lavg_array[lavg_num].l_until = end_of_cur_int; + clear_run_if_late(line->cl_option); + } + } + else + lavg_array[lavg_num].l_until = + (line->cl_until > 0) ? now + line->cl_until : 0; + lavg_num++; } @@ -502,11 +532,11 @@ set_wday(struct tm *date) void -goto_non_matching(CL *line, struct tm *ftime) +goto_non_matching(CL *line, struct tm *ftime, char option) /* search the first the nearest time and date that does * not match the line */ { - if ( is_freq_periodically(line->cl_option)) { + if ( is_freq_periodically(line->cl_option) && option != END_OF_INTERVAL) { int max = get_nb_mdays(ftime->tm_year, ftime->tm_mon); if (is_freq_mid(line->cl_option)) { if (is_freq_mins(line->cl_option)) @@ -593,7 +623,7 @@ goto_non_matching(CL *line, struct tm *ftime) } if (debug_opt) set_wday(ftime); - debug(" %s first non matching %d/%d/%d wday:%d %02d:%02d", + debug(" %s beginning of next period %d/%d/%d wday:%d %02d:%02d", line->cl_shell, (ftime->tm_mon + 1), ftime->tm_mday, (ftime->tm_year + 1900), ftime->tm_wday, ftime->tm_hour, ftime->tm_min); @@ -612,6 +642,10 @@ goto_non_matching(CL *line, struct tm *ftime) char ignore_mons = (is_freq_mons(line->cl_option)) ? 1:0; char ignore_dow = (is_freq_dow(line->cl_option)) ? 1:0; + if (option == END_OF_INTERVAL) + /* we want to go to the end of the current interval */ + ignore_mins=ignore_hrs=ignore_days=ignore_mons=ignore_dow = 0; + /* */ debug(" ignore: %d %d %d %d %d", ignore_mins, ignore_hrs, ignore_days, ignore_mons, ignore_dow); @@ -671,9 +705,15 @@ goto_non_matching(CL *line, struct tm *ftime) } } } - - debug(" %s first non matching %d/%d/%d wday:%d %02d:%02d", - line->cl_shell, (ftime->tm_mon + 1), ftime->tm_mday, + + if (option == END_OF_INTERVAL) + /* we want the end of the current interval, not the beginning + * of the first non-matching interval */ + ftime->tm_min--; + + debug(" %s %s %d/%d/%d wday:%d %02d:%02d", line->cl_shell, + (option == STD) ? "first non matching" : "end of interval", + (ftime->tm_mon + 1), ftime->tm_mday, (ftime->tm_year + 1900), ftime->tm_wday, ftime->tm_hour, ftime->tm_min); return; @@ -682,7 +722,7 @@ goto_non_matching(CL *line, struct tm *ftime) void -set_next_exe(CL *line, char is_new_line) +set_next_exe(CL *line, char option) /* set the cl_nextexe of a given CL and insert it in the queue */ { @@ -708,8 +748,8 @@ set_next_exe(CL *line, char is_new_line) /* to prevent multiple execution in the same minute */ ftime.tm_min += 1; ftime.tm_sec = 0; - if ( line->cl_runfreq == 1 && ! is_new_line ) - goto_non_matching(line, &ftime); + if (line->cl_runfreq==1 && option != NO_GOTO && option != NO_GOTO_LOG) + goto_non_matching(line, &ftime, STD); setMonth: for (i = ftime.tm_mon; (bit_test(line->cl_mons, i)==0) && (i<12); i++); @@ -842,7 +882,7 @@ set_next_exe(CL *line, char is_new_line) set_cl_nextexe: line->cl_nextexe = mktime(&ftime); - if ( ! is_new_line ) + if ( option != NO_GOTO ) debug(" cmd: %s next exec %d/%d/%d wday:%d %02d:%02d", line->cl_shell, (ftime.tm_mon + 1), ftime.tm_mday, (ftime.tm_year + 1900), ftime.tm_wday, @@ -856,20 +896,129 @@ set_next_exe(CL *line, char is_new_line) line->cl_timefreq); } - insert_nextexe(line); - +} + + +void +set_next_exe_notrun(CL *line, char context) + /* set the time of the next execution and send a mail to tell him the job + * has not run if necessary */ +{ + time_t next_period = 0; + struct tm *ft = NULL; + struct tm ftime, last_nextexe; + char set_next_exe_opt = 0; + +/* // */ + debug(" set_next_exe_notrun : %s %d", line->cl_shell, context); +/* // */ + + if (context == SYSDOWN) { + ft = localtime(&line->cl_nextexe); + set_next_exe_opt = NO_GOTO; + } + else { + ft = localtime(&now); + set_next_exe_opt = NO_GOTO_LOG; + } + + /* localtime() function seem to return every time the same pointer : + it resets our previous changes, so we need to prevent it + ( localtime() is used in the debug() function) */ + memcpy(&ftime, ft, sizeof(struct tm)); + /* we also copy it to last_nextexe which will be used in mail_notrun */ + memcpy(&last_nextexe, ft, sizeof(struct tm)); + + ftime.tm_sec = 0; + goto_non_matching(line, &ftime, STD); + next_period = mktime(&ftime); + + set_next_exe(line, set_next_exe_opt); + if ( line->cl_nextexe >= next_period ) { + /* line has not run during one or more period(s) : send a mail */ + mail_notrun(line, context, &last_nextexe); + } } +void +mail_notrun(CL *line, char context, struct tm *last_nextexe) + /* send a mail to tell user a %-job has not run (and why) */ +{ + int pid = 0; + int fd = 0; + + switch ( pid = fork() ) { + case -1: + error_e("Fork error : could not mail for not run %s", line->cl_shell); + return; + case 0: + /* child */ + break; + default: + /* parent */ + +/* // */ + debug("Reporting by mail non execution of %s (pid %d)", + line->cl_shell, pid); +/* // */ + + /* create a entry in exe_array */ + if ( exe_num >= exe_array_size ) { + struct exe *ptr = NULL; + short int old_size = exe_array_size; + + debug("Resizing exe_array"); + exe_array_size = (exe_array_size + EXE_GROW_SIZE); + + if ( (ptr = calloc(exe_array_size, sizeof(struct exe))) == NULL ) + die_e("could not calloc exe_array"); + + memcpy(ptr, exe_array, (sizeof(struct exe) * old_size)); + free(exe_array); + exe_array = ptr; + } + /* set line to NULL as this is not a line ... */ + exe_array[exe_num].e_line = NULL; + exe_array[exe_num].e_pid = pid; + exe_num++; + return; + } + + if ( last_nextexe == NULL ) + last_nextexe = localtime(&line->cl_nextexe); + + /* create a temp file, and write in it the message to send */ + fd = create_mail(line, "Non-execution of fcron job"); + + if (context == SYSDOWN) { + xwrite(fd, "Line has not run since x due to system's down state.\n"); + } + else if (context == LAVG) { + xwrite(fd, "Line has not run since x due to a too high system load" + " average or too many lavg-serial job.\n"); + } + + /* become user (for security reasons) */ + if (change_user(line->cl_runas) < 0) + return ; + + /* then, send mail */ + launch_mailer(line, fd); + + /* we should not come here : launch_mailer does not return */ + error("mail_notrun : launch_mailer failed"); + +} time_t check_lavg(time_t lim) /* run a job based on system load average if one should be run * and return the time to sleep */ { - time_t tts = time_to_sleep(lim); + time_t tts = 0; #ifdef NOLOADAVG while ( lavg_num > 0 ) @@ -886,13 +1035,40 @@ check_lavg(time_t lim) /* first, check if some lines must be executed because of until */ while ( i < lavg_num ) - if ( lavg_array[i].l_line->cl_until && lavg_array[i].l_until < now ) { - debug("until %s %d", lavg_array[i].l_line->cl_shell, - lavg_array[i].l_until); - run_lavg_job(i); + if ( (lavg_array[i].l_line->cl_until > 0 + || lavg_array[i].l_line->cl_runfreq == 1) + && lavg_array[i].l_until < now){ + if ( ! is_run_if_late(lavg_array[i].l_line->cl_option) ) { + explain("Interval of execution exceeded : %s (not run)", + lavg_array[i].l_line->cl_shell); + + /* set time of the next execution and send a mail if needed */ + if ( is_notice_notrun(lavg_array[i].l_line->cl_option) ) + set_next_exe_notrun(lavg_array[i].l_line, LAVG); + else + set_next_exe(lavg_array[i].l_line, NO_GOTO_LOG); + + /* remove this job from the lavg queue */ + lavg_array[i].l_line->cl_numexe -= 1; + if (i < --lavg_num) { + lavg_array[i] = lavg_array[lavg_num]; + lavg_array[lavg_num].l_line = NULL; + } + else + lavg_array[i].l_line = NULL; + + } + else { + debug("until %s %d", lavg_array[i].l_line->cl_shell, + lavg_array[i].l_until); + run_lavg_job(i); + } } else i++; + /* we do this set here as the nextexe of lavg line may change before */ + tts = time_to_sleep(lim); + if ( lavg_num == 0 ) return tts; diff --git a/option.h b/option.h index 2d15275..c53faa7 100644 --- a/option.h +++ b/option.h @@ -21,7 +21,7 @@ * `LICENSE' that comes with the fcron source distribution. */ - /* $Id: option.h,v 1.14 2001-01-15 18:46:39 thib Exp $ */ + /* $Id: option.h,v 1.15 2001-01-27 15:45:08 thib Exp $ */ /* This has been inspired from bitstring(3) : here is the original copyright : */ @@ -64,7 +64,7 @@ 6 is this job should be run serially only once (for bootrun) ? 7 does the output have to be mailed to user ? 8 does the output (even if zero-length) must be mailed to user ? - 9 ****** bit 9 unused ********************** + 9 if time of execution is exceeded, exec the lavg job or not ? 10 can this job be executed several times simultaneously 11 can this job be put several times in the serial queue simultaneously 12 can this job be put several times in the lavg queue simultaneously @@ -220,8 +220,15 @@ /* - bit 9 : currently unused + bit 9 : set to 1 : exec the job now if time of execution is exceeded + set to 0 : do not exec the job if time of execution is exceeded */ +#define is_run_if_late(opt) \ + (_bit_test(opt, 9)) +#define set_run_if_late(opt) \ + (_bit_set(opt, 9)) +#define clear_run_if_late(opt) \ + (_bit_clear(opt, 9)) /* @@ -375,11 +382,11 @@ set to 0 : let the job in the %-queue if interval is exceeded */ #define is_strict(opt) \ - (_bit_test(opt, 20)) + ( ! _bit_test(opt, 20)) #define set_strict(opt) \ - (_bit_set(opt, 20)) -#define clear_strict(opt) \ (_bit_clear(opt, 20)) +#define clear_strict(opt) \ + (_bit_set(opt, 20)) /* -- 2.40.0