* `LICENSE' that comes with the fcron source distribution.
*/
- /* $Id: conf.c,v 1.3 2000-05-30 19:25:41 thib Exp $ */
+ /* $Id: conf.c,v 1.4 2000-05-31 19:11:15 thib Exp $ */
#include "fcron.h"
memcmp(new_l->cl_mins, old_l->cl_mins, size) == 0
) {
- new_l->cl_remain = old_l->cl_remain;
- new_l->cl_nextexe = old_l->cl_nextexe;
+ new_l->cl_remain = old_l->cl_remain;
+ if ( (new_l->cl_nextexe = old_l->cl_nextexe) > 0 )
+ insert_nextexe(new_l);
+ else insert_freq(new_l);
if (debug_opt) {
if (new_l->cl_nextexe > 0) {
if ( cl->cl_nextexe <= ti )
set_next_exe(cl, 1);
} else {
-
+ insert_freq(cl);
debug(" remain: %ld timefreq: %ld", cl->cl_remain,
cl->cl_timefreq);
}
/* we don't set cl_nextexe to 0 because this value is
* reserved to the entries based on frequency */
cl->cl_nextexe = 1;
+ insert_nextexe(cl);
cl->cl_pid = cl->cl_mailfd = cl->cl_mailpid = 0;
}
}
cl->cl_next = cf->cf_line_base;
cf->cf_line_base = cl;
+ cl->cl_file = cf;
Alloc(cl, CL);
}
/* free last calloc : unused */
CL *cur_line;
env_t *env = NULL;
env_t *cur_env = NULL;
+ struct job *j = NULL;
+ struct job *prev_j = NULL;
file = file_base;
while ( file != NULL) {
cur_line = file->cf_line_base;
while ( (line = cur_line) != NULL) {
cur_line = line->cl_next;
+
+ /* remove line from the lists */
+ if( line->cl_runfreq != 0 ) {
+ for ( j = freq_base; j != NULL; j = j->j_next )
+ if ( j->j_line == line ) {
+ prev_j->j_next = j->j_next;
+ free(j);
+ break;
+ }
+ }
+ else
+ for ( j = queue_base; j != NULL; j = j->j_next )
+ if ( j->j_line == line ) {
+ prev_j->j_next = j->j_next;
+ free(j);
+ break;
+ }
+
+ /* free line itself */
free(line->cl_shell);
free(line);
}
/* file not in list */
return;
- /* remove file from list */
+ /* remove file from file list */
if (prev_file == NULL)
file_base = file->cf_next;
else
* `LICENSE' that comes with the fcron source distribution.
*/
- /* $Id: database.c,v 1.5 2000-05-30 19:26:21 thib Exp $ */
+ /* $Id: database.c,v 1.6 2000-05-31 19:11:35 thib Exp $ */
#include "fcron.h"
test_jobs(time_t t2)
/* determine which jobs need to be run, and run them. */
{
- CF *file;
- CL *line;
+ struct job *j = NULL;
debug("Looking for jobs to execute ...");
- for (file = file_base; file; file = file->cf_next) {
- debug("FILE %s:", file->cf_user);
-
- for (line = file->cf_line_base; line; line = line->cl_next) {
-
- if ( (line->cl_nextexe <= t2) && (line->cl_nextexe != 0) ) {
-
- set_next_exe(line, 0);
-
- if (--(line->cl_remain) > 0) {
- debug(" cl_Remain: %d", line->cl_remain);
- continue ;
- }
-
- if (line->cl_pid > 0) {
- warn(" process already running: %s '%s'",
- file->cf_user,
- line->cl_shell
- );
- line->cl_remain = line->cl_runfreq;
- }
- else {
- line->cl_remain = line->cl_runfreq;
-
- run_job(file, line);
-
- }
- }
-
- /* jobs based on timefreq */
- else if (line->cl_timefreq > 0 && line->cl_remain <= 0 ) {
-
- line->cl_remain = line->cl_timefreq;
-
- if (line->cl_pid > 0) {
- warn(" process already running: %s '%s'",
- file->cf_user,
- line->cl_shell
- );
- } else {
-
- run_job(file, line);
-
- }
- }
-
+ /* job based on date & time */
+ for (j=queue_base; (j!=NULL)&&(j->j_line->cl_nextexe <= t2); j=j->j_next){
+ set_next_exe(j->j_line, 0);
+ if (--(j->j_line->cl_remain) > 0) {
+ debug(" cl_Remain: %d", j->j_line->cl_remain);
+ continue ;
+ }
+ if (j->j_line->cl_pid > 0) {
+ warn(" process already running: %s '%s'",
+ j->j_line->cl_file->cf_user,
+ j->j_line->cl_shell
+ );
+ j->j_line->cl_remain = j->j_line->cl_runfreq;
+ }
+ else {
+ j->j_line->cl_remain = j->j_line->cl_runfreq;
+ run_job(j->j_line);
+ }
+ }
+ /* job based on frequency */
+ for (j=freq_base; (j!=NULL)&&(j->j_line->cl_remain <= 0); j=j->j_next){
+ j->j_line->cl_remain = j->j_line->cl_timefreq;
+ insert_freq(j->j_line);
+ if (j->j_line->cl_pid > 0) {
+ warn(" process already running: %s '%s'",
+ j->j_line->cl_file->cf_user,
+ j->j_line->cl_shell
+ );
+ } else {
+ run_job(j->j_line);
}
-
}
}
wait_chld(void)
/* wait_chld() - check for job completion */
{
- CF *file;
- CL *line;
+ struct job *j = NULL;
int pid;
int status;
debug("wait_chld");
///////
- while ( (pid=wait3(&status, WNOHANG, NULL)) > 0 ) {
-
- for (file = file_base; file; file = file->cf_next) {
-
- for (line = file->cf_line_base; line && file->cf_running;
- line = line->cl_next) {
-
- if (pid < 0 || pid == line->cl_pid) {
- end_job(file, line, status);
- goto nextloop;
- }
- else if ( pid == line->cl_mailpid ) {
- end_mailer(line, status);
- goto nextloop;
- }
-
+ while ( (pid = wait3(&status, WNOHANG, NULL)) > 0 ) {
+ for (j = exe_base; j != NULL ; j = j->j_next) {
+ if (pid < 0 || pid == j->j_line->cl_pid) {
+ end_job(j->j_line, status);
+ goto nextloop;
}
-
- }
-
+ else if ( pid == j->j_line->cl_mailpid ) {
+ end_mailer(j->j_line, status);
+ goto nextloop;
+ }
+ }
nextloop:
}
wait_all(int *counter)
/* return after all jobs completion. */
{
- CF *file;
- CL *line;
- pid_t pid;
+ struct job *j = NULL;
+ int pid;
int status;
debug("Waiting for all jobs");
- while ( (*counter > 0) && (pid=wait3(&status, 0, NULL)) > 0 ) {
-
- for (file = file_base; file; file = file->cf_next) {
+ while ( (*counter > 0) && (pid = wait3(&status, 0, NULL)) > 0 ) {
- if (file->cf_running) {
+ for (j = exe_base; j != NULL ; j = j->j_next) {
- for (line = file->cf_line_base; line; line = line->cl_next) {
-
- if (pid < 0 || pid == line->cl_pid)
- end_job(file, line, status);
- else if ( pid == line->cl_mailpid )
- end_mailer(line, status);
-
- }
- }
+ if (pid < 0 || pid == j->j_line->cl_pid)
+ end_job(j->j_line, status);
+ else if ( pid == j->j_line->cl_mailpid )
+ end_mailer(j->j_line, status);
}
}
void
set_next_exe(CL *line, char is_new_line)
- /* set the cl_nextexe of a given CL */
+ /* set the cl_nextexe of a given CL and insert it in the queue */
{
if (line->cl_timefreq == 0) {
register int i;
int max;
-
now = time(NULL);
ft = localtime(&now);
line->cl_nextexe = mktime(&ftime);
+ insert_nextexe(line);
+
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,
}
+void
+insert_nextexe(CL *line)
+ /* insert a job based on time and date in the corresponding queue list */
+{
+ struct job *newjob = NULL;
+
+ if (queue_base != NULL) {
+ struct job *j = NULL;
+ struct job *jprev = NULL;
+ struct job *old_entry = NULL;
+
+ j = queue_base;
+ while (j != NULL && (line->cl_nextexe > j->j_line->cl_nextexe)) {
+ if ( j->j_line == line ) {
+ old_entry = j;
+ jprev->j_next = j->j_next;
+ j = jprev;
+ }
+ jprev = j;
+ j = j->j_next;
+ }
+ if (old_entry == NULL) {
+ /* this job wasn't in the queue : we append it */
+ Alloc(newjob, job);
+ newjob->j_line = line;
+ }
+ else
+ /* this job was already in the queue : we move it */
+ newjob = old_entry;
+
+ newjob->j_next = j;
+
+ if (jprev == NULL)
+ queue_base = newjob;
+ else
+ jprev->j_next = newjob;
+
+ }
+ else {
+ /* no job in queue */
+ Alloc(newjob, job);
+ newjob->j_line = line;
+ queue_base = newjob;
+ }
+
+}
+
+void
+insert_freq(CL *line)
+ /* insert a job based on frequency in the corresponding queue list */
+{
+ struct job *newjob = NULL;
+
+ /* insert job in the queue */
+ if (freq_base != NULL) {
+ struct job *j = NULL;
+ struct job *jprev = NULL;
+ struct job *old_entry = NULL;
+
+ j = freq_base;
+ while (j != NULL && (line->cl_remain > j->j_line->cl_remain)) {
+ if ( j->j_line == line ) {
+ old_entry = j;
+ jprev->j_next = j->j_next;
+ j = jprev;
+ }
+ jprev = j;
+ j = j->j_next;
+ }
+ if (old_entry == NULL) {
+ /* this job wasn't in the queue : we append it */
+ Alloc(newjob, job);
+ newjob->j_line = line;
+ }
+ else
+ /* this job was already in the queue : we move it */
+ newjob = old_entry;
+
+ newjob->j_next = j;
+
+ if (jprev == NULL)
+ freq_base = newjob;
+ else
+ jprev->j_next = newjob;
+ }
+ else {
+ Alloc(newjob, job);
+ newjob->j_line = line;
+ freq_base = newjob;
+ }
+}
long
time_to_sleep(short lim)
/* return the time to sleep until next task have to be executed. */
{
-
- CF *file;
- CL *line;
/* we set tts to a big value, unless some problems can occurs
* with files without any line */
time_t tts = lim;
- time_t cur;
time_t now;
now = time(NULL);
- for (file = file_base; file; file = file->cf_next) {
-
- for (line = file->cf_line_base; line; line = line->cl_next) {
-
- if (line->cl_nextexe > 0)
- cur = (line->cl_nextexe > now) ? (line->cl_nextexe - now) : 0;
- else
- cur = line->cl_remain;
-
- if (cur < tts)
- tts = cur;
-
- if (tts == 0)
- return 0;
-
- }
-
+ if (queue_base == NULL && freq_base == NULL)
+ /* no lines : we sleep as much as we can */
+ goto end;
+ else if (queue_base == NULL && freq_base != NULL) {
+ tts = freq_base->j_line->cl_remain;
+ goto end;
+ }
+ else if (queue_base != NULL && freq_base == NULL) {
+ if ( (tts = queue_base->j_line->cl_nextexe - now) < 0 ) tts = 0;
+ goto end;
}
+
+ /* we have freq lines and normal lines */
+ if(queue_base->j_line->cl_nextexe - now < freq_base->j_line->cl_remain) {
+ if ( (tts = queue_base->j_line->cl_nextexe - now) < 0 ) tts = 0;
+ }
+ else
+ tts = freq_base->j_line->cl_remain;
+ end:
debug("Time to sleep: %lds", tts);
return tts;
update_time_remaining(long dt)
/* update the remaining time of tasks run at a certain frequency */
{
- CF *file;
- CL *line;
- debug("Updating time remaining ...");
-
- for (file = file_base; file; file = file->cf_next) {
+ struct job *j = freq_base;
- debug("File %s", file->cf_user);
+ debug("Updating time remaining ...");
- for (line = file->cf_line_base; line; line = line->cl_next) {
- if (line->cl_timefreq > 0) {
+ for (j=freq_base; j != NULL; j = j->j_next ) {
- if ( (line->cl_remain - dt) >= 0 )
- line->cl_remain -= dt;
- else
- line->cl_remain = 0;
+ if ( (j->j_line->cl_remain - dt) >= 0 )
+ j->j_line->cl_remain -= dt;
+ else
+ j->j_line->cl_remain = 0;
- debug(" '%s' cl_remain = %d", line->cl_shell,
- line->cl_remain);
- }
- }
+ debug(" '%s' cl_remain = %d", j->j_line->cl_shell,
+ j->j_line->cl_remain);
}
* `LICENSE' that comes with the fcron source distribution.
*/
- /* $Id: fcron.c,v 1.8 2000-05-30 19:26:28 thib Exp $ */
+ /* $Id: fcron.c,v 1.9 2000-05-31 19:11:40 thib Exp $ */
#include "fcron.h"
-char rcs_info[] = "$Id: fcron.c,v 1.8 2000-05-30 19:26:28 thib Exp $";
+char rcs_info[] = "$Id: fcron.c,v 1.9 2000-05-31 19:11:40 thib Exp $";
void main_loop(void);
void info(void);
void usage(void);
-void xexit(int exit_value);
void sighup_handler(int x);
void sigterm_handler(int x);
void sigchild_handler(int x);
void get_lock(void);
+/* command line options */
char debug_opt = DEBUG; /* set to 1 if we are in debug mode */
char foreground = FOREGROUND; /* set to 1 when we are on foreground, else 0 */
-char *cdir = FCRONTABS; /* the dir where are stored users' fcrontabs */
+char *cdir = FCRONTABS; /* the dir where are stored users' fcrontabs */
+
+/* process identity */
int daemon_uid;
pid_t daemon_pid;
char *prog_name = NULL;
-char sig_conf = 0; /* is 1 when we got a SIGHUP */
+
+/* have we got a signal ? */
+char sig_conf = 0; /* is 1 when we got a SIGHUP */
char sig_chld = 0; /* is 1 when we got a SIGCHLD */
-CF *file_base; /* point to the first file of the list */
+
+/* jobs database */
+struct CF *file_base; /* point to the first file of the list */
+struct job *queue_base; /* ordered list of normal jobs to be run */
+struct job *serial_base; /* ordered list of job to be run one by one */
+struct job *freq_base; /* ordered list of jobs based on frequency */
+struct job *exe_base; /* jobs which are executed */
+
time_t t1; /* the time at which sleep began */
if (sig_conf == 1)
/* update configuration */
synchronize_dir(".");
- else {
+ else
/* reload all configuration */
- update_time_remaining(dt);
reload_all(".");
- }
+
sig_conf = 0;
}
* `LICENSE' that comes with the fcron source distribution.
*/
- /* $Id: fcron.h,v 1.4 2000-05-30 19:26:48 thib Exp $ */
+ /* $Id: fcron.h,v 1.5 2000-05-31 19:11:42 thib Exp $ */
#ifndef __FCRONH__
#define __FCRONH__
extern char *prog_name;
extern char sig_hup;
extern CF *file_base;
+extern struct job *queue_base;
+extern struct job *serial_base;
+extern struct job *freq_base;
+extern struct job *exe_base;
/* end of global variables */
extern void wait_all(int *counter);
extern time_t time_to_sleep(short lim);
extern void set_next_exe(CL *line, char is_new_line);
+extern void insert_nextexe(CL *line);
+extern void insert_freq(CL *line);
extern void update_time_remaining(long dt);
/* end of database.c */
/* end of conf.c */
/* job.c */
-extern void run_job(CF *file, CL *line);
-extern void end_job(CF *file, CL *line, int status);
+extern void run_job(CL *line);
+extern void end_job(CL *line, int status);
extern void end_mailer(CL *line, int status);
/* end of job.c */
* `LICENSE' that comes with the fcron source distribution.
*/
- /* $Id: global.h,v 1.4 2000-05-30 19:27:28 thib Exp $ */
+ /* $Id: global.h,v 1.5 2000-05-31 19:11:44 thib Exp $ */
+
+
+/*
+ WARNING : this file should not be modified.
+ Compilation's options are in config.h
+*/
#ifndef __GLOBALH__
#define __GLOBALH__
#include <unistd.h>
#include <fcntl.h>
-#include "bitstring.h"
-
#include "config.h"
+#include "bitstring.h"
+#include "option.h"
-
-/* none configurable constants */
-#define FILEVERSION "001" /* syntax's version of fcrontabs :
+#define FILEVERSION "002" /* syntax's version of fcrontabs :
* must have a length of 3 characters */
#define debug if(debug_opt) Debug
+
typedef struct env_t {
char *e_name; /* env name */
char *e_val; /* env value */
struct CL *cf_line_base;
char *cf_user; /* user-name */
char *cf_mailto; /* mail output's to mail_user */
+ short int cf_mailpos; /* 'empty mail file' size */
struct env_t *cf_env_base; /* list of all env variables to set */
int cf_running; /* number of jobs running */
} CF;
* because some tests made are dependent to that order */
typedef struct CL {
struct CL *cl_next;
+ struct CF *cl_file; /* the file in which the line is */
+ char option; /* options for that line (see option.h) */
char *cl_shell; /* shell command */
pid_t cl_pid; /* running pid, 0, or armed (-1) */
pid_t cl_mailpid; /* mailer pid or 0 */
int cl_mailfd; /* running pid is for mail */
- int cl_mailpos; /* 'empty file' size */
/* see bitstring(3) man page for more details */
bitstr_t bit_decl(cl_mins, 60); /* 0-59 */
bitstr_t bit_decl(cl_hrs, 24); /* 0-23 */
time_t cl_nextexe; /* time and date of the next execution */
} CL;
+typedef struct job {
+ struct CL *j_line;
+ struct job *j_next;
+} job;
#endif /* __GLOBALH__ */
* `LICENSE' that comes with the fcron source distribution.
*/
- /* $Id: job.c,v 1.5 2000-05-30 19:27:30 thib Exp $ */
+ /* $Id: job.c,v 1.6 2000-05-31 19:11:47 thib Exp $ */
#include "fcron.h"
int temp_file(void);
void xwrite(int fd, char *string);
-void launch_mailer(CF *file, CL *line);
+void launch_mailer(CL *line);
int change_user(const char *user, short dochdir);
void
-run_job(CF *file, CL *line)
+run_job(CL *line)
/* fork(), redirect outputs to a temp file, and execl() the task */
{
char *shell = NULL;
char *home = NULL;
env_t *env = NULL;
+ struct job *j = NULL;
/* create temporary file for stdout and stderr of the job */
line->cl_mailfd = temp_file();
/* write mail header */
xwrite(line->cl_mailfd,"To: ");
- xwrite(line->cl_mailfd, file->cf_user);
+ xwrite(line->cl_mailfd, line->cl_file->cf_user);
xwrite(line->cl_mailfd, "\nSubject: Output of fcron job: '");
xwrite(line->cl_mailfd, line->cl_shell);
xwrite(line->cl_mailfd,"'\n\n");
- line->cl_mailpos = lseek(line->cl_mailfd, 0, SEEK_END);
+ if ( ! line->cl_file->cf_mailpos )
+ line->cl_file->cf_mailpos = ( lseek(line->cl_mailfd, 0, SEEK_END)
+ - strlen(line->cl_shell) );
+
+
+ /* append job to the list of executed job */
+ Alloc(j, job);
+ j->j_line = line;
+ j->j_next = exe_base;
+ exe_base = j;
switch ( pid = fork() ) {
/* child */
foreground = 0;
- if (change_user(file->cf_user, 1) < 0)
+ if (change_user(line->cl_file->cf_user, 1) < 0)
return ;
/* stdin is already /dev/null, setup stdout and stderr */
if ( close(2) != 0 )
die_e("Can't close file descriptor %d",2);
- if ( file->cf_mailto != NULL && strcmp(file->cf_mailto, "") == 0 ) {
+ if ( line->cl_file->cf_mailto != NULL &&
+ strcmp(line->cl_file->cf_mailto, "") == 0 ) {
+
if ( close(line->cl_mailfd) != 0 )
die_e("Can't close file descriptor %d", line->cl_mailfd);
if ( (line->cl_mailfd = open("/dev/null", O_RDWR)) < 0 )
die_e("open: /dev/null:");
+
}
if (dup2(line->cl_mailfd, 1) != 1 || dup2(line->cl_mailfd, 2) != 2)
xcloselog();
/* set env variables */
- for ( env = file->cf_env_base; env; env = env->e_next)
+ for ( env = line->cl_file->cf_env_base; env; env = env->e_next)
if ( setenv(env->e_name, env->e_val, 1) != 0 )
error("could not setenv()");
line->cl_pid = pid;
////////////
- debug(" cf_running: %d", file->cf_running);
+ debug(" cf_running: %d", line->cl_file->cf_running);
///////////
- file->cf_running += 1;
+ line->cl_file->cf_running += 1;
explain(" Job `%s' started (pid %d)", line->cl_shell, line->cl_pid);
}
void
-end_job(CF *file, CL *line, int status)
+end_job(CL *line, int status)
/* if task have made some output, mail it to user */
{
debug(" end_job");
//////
- if ( lseek(line->cl_mailfd, 0, SEEK_END) > line->cl_mailpos ) {
- if ( file->cf_mailto != NULL && file->cf_mailto[0] == '\0' )
+ if ( ( lseek(line->cl_mailfd, 0, SEEK_END) - strlen (line->cl_shell) )
+ > line->cl_file->cf_mailpos ) {
+ if ( line->cl_file->cf_mailto != NULL &&
+ line->cl_file->cf_mailto[0] == '\0' )
/* there is a mail output, but it will not be mail */
mail_output = 2;
else
else /* is this possible? */
error("Job `%s' terminated abnormally %s", line->cl_shell, m);
- if (mail_output == 1) launch_mailer(file, line);
+ if (mail_output == 1) launch_mailer(line);
/* if MAILTO is "", temp file is already closed */
if ( mail_output != 2 && close(line->cl_mailfd) != 0 )
line->cl_pid = 0;
////////////
- debug(" cf_running: %d", file->cf_running);
+ debug(" cf_running: %d", line->cl_file->cf_running);
- file->cf_running -= 1;
+ line->cl_file->cf_running -= 1;
}
void
-launch_mailer(CF *file, CL *line)
+launch_mailer(CL *line)
/* mail the output of a job to user */
{
char *mailto = NULL;
xcloselog();
/* determine which will be the mail receiver */
- if ( (mailto = file->cf_mailto) == NULL )
- mailto = file->cf_user;
+ if ( (mailto = line->cl_file->cf_mailto) == NULL )
+ mailto = line->cl_file->cf_user;
/* change permissions */
- if (change_user(file->cf_user, 1) < 0)
+ if (change_user(line->cl_file->cf_user, 1) < 0)
return ;
/* run sendmail with mail file as standard input */