关于crond的介绍和使用,请百度。这里分析下如何实现。
下载源码:http://down1.chinaunix.net/distfiles/vixie-cron-4.1.tar.bz2
该源码中主要由以下两个主要的.c组成:
1:cron.c
2:crontab.c
首先看下crontab.c,这个文件主要实现了crontab命令,来管理配置文件。
main函数:
int
main(int argc, char *argv[]) {
int exitstatus;
Pid = getpid();
ProgramName = argv[0 ];
setlocale(LC_ALL, "" );
#if defined(BSD)
setlinebuf(stderr);
#endif
parse_args(argc, argv);
set_cron_cwd();
if (!allowed(RealUser, CRON_ALLOW, CRON_DENY)) {
fprintf (stderr,
"You (%s) are not allowed to use this program (%s)\n" ,
User, ProgramName);
fprintf (stderr, "See crontab(1) for more information\n" );
log_it(RealUser, Pid, "AUTH" , "crontab command not allowed" );
exit (ERROR_EXIT);
}
exitstatus = OK_EXIT;
switch (Option) {
case opt_unknown:
exitstatus = ERROR_EXIT;
break ;
case opt_list:
list_cmd();
break ;
case opt_delete:
delete_cmd();
break ;
case opt_edit:
edit_cmd();
break ;
case opt_replace:
if (replace_cmd() < 0 )
exitstatus = ERROR_EXIT;
break ;
default :
abort ();
}
exit (exitstatus);
}
1. 解析函数入参,并设置option变量。
2. 根据解析出的option变量,分别调用对应的函数。涉及到的函数实现为列出配置、删除配置、指定配置文件、编辑配置文件。
列出配置和删除配置相对简单,分别是读取文件和删除文件。不做分析,接下来主要看下如何编辑配置文件和指定配置文件。
编辑文件:
static void
edit_cmd(void ) {
char n[MAX_FNAME], q[MAX_TEMPSTR], *editor;
FILE *f;
int ch, t, x;
struct stat statbuf;
struct utimbuf utimebuf;
WAIT_T waiter;
PID_T pid, xpid;
log_it(RealUser, Pid, "BEGIN EDIT" , User);
if (!glue_strings(n, sizeof n, SPOOL_DIR, User, '/' )) {
fprintf (stderr, "path too long\n" );
exit (ERROR_EXIT);
}
if (!(f = fopen(n, "r" ))) {
if (errno != ENOENT) {
perror(n);
exit (ERROR_EXIT);
}
fprintf (stderr, "no crontab for %s - using an empty one\n" ,
User);
if (!(f = fopen(_PATH_DEVNULL, "r" ))) {
perror(_PATH_DEVNULL);
exit (ERROR_EXIT);
}
}
if (fstat(fileno(f), &statbuf) < 0 ) {
perror("fstat" );
goto fatal;
}
utimebuf.actime = statbuf.st_atime;
utimebuf.modtime = statbuf.st_mtime;
(void )signal(SIGHUP, SIG_IGN);
(void )signal(SIGINT, SIG_IGN);
(void )signal(SIGQUIT, SIG_IGN);
if (!glue_strings(Filename, sizeof Filename, _PATH_TMP,
"crontab.XXXXXXXXXX" , '/' )) {
fprintf (stderr, "path too long\n" );
goto fatal;
}
if (-1 == (t = mkstemp(Filename))) {
perror(Filename);
goto fatal;
}
#ifdef HAS_FCHOWN
if (fchown(t, MY_UID(pw), MY_GID(pw)) < 0 ) {
perror("fchown" );
goto fatal;
}
#else
if (chown(Filename, MY_UID(pw), MY_GID(pw)) < 0 ) {
perror("chown" );
goto fatal;
}
#endif
if (!(NewCrontab = fdopen(t, "r+" ))) {
perror("fdopen" );
goto fatal;
}
Set_LineNum(1 )
x = 0 ;
while (EOF != (ch = get_char(f))) {
if ('#' != ch) {
putc(ch, NewCrontab);
break ;
}
while (EOF != (ch = get_char(f)))
if (ch == '\n' )
break ;
if (++x >= NHEADER_LINES)
break ;
}
if (EOF != ch)
while (EOF != (ch = get_char(f)))
putc(ch, NewCrontab);
fclose(f);
if (fflush(NewCrontab) < OK) {
perror(Filename);
exit (ERROR_EXIT);
}
utime(Filename, &utimebuf);
again:
rewind(NewCrontab);
if (ferror(NewCrontab)) {
fprintf (stderr, "%s: error while writing new crontab to %s\n" ,
ProgramName, Filename);
fatal:
unlink(Filename);
exit (ERROR_EXIT);
}
if (((editor = getenv("VISUAL" )) == NULL || *editor == '\0' ) &&
((editor = getenv("EDITOR" )) == NULL || *editor == '\0' )) {
editor = EDITOR;
}
switch (pid = fork()) {
case -1 :
perror("fork" );
goto fatal;
case 0 :
if (setgid(MY_GID(pw)) < 0 ) {
perror("setgid(getgid())" );
exit (ERROR_EXIT);
}
if (setuid(MY_UID(pw)) < 0 ) {
perror("setuid(getuid())" );
exit (ERROR_EXIT);
}
if (chdir(_PATH_TMP) < 0 ) {
perror(_PATH_TMP);
exit (ERROR_EXIT);
}
if (!glue_strings(q, sizeof q, editor, Filename, ' ' )) {
fprintf (stderr, "%s: editor command line too long\n" ,
ProgramName);
exit (ERROR_EXIT);
}
execlp(_PATH_BSHELL, _PATH_BSHELL, "-c" , q, (char *)0 );
perror(editor);
exit (ERROR_EXIT);
default :
break ;
}
for (;;) {
xpid = waitpid(pid, &waiter, WUNTRACED);
if (xpid == -1 ) {
if (errno != EINTR)
fprintf (stderr, "%s: waitpid() failed waiting for PID %ld from \"%s\": %s\n" ,
ProgramName, (long )pid, editor, strerror(errno));
} else if (xpid != pid) {
fprintf (stderr, "%s: wrong PID (%ld != %ld) from \"%s\"\n" ,
ProgramName, (long )xpid, (long )pid, editor);
goto fatal;
} else if (WIFSTOPPED(waiter)) {
kill(getpid(), WSTOPSIG(waiter));
} else if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) {
fprintf (stderr, "%s: \"%s\" exited with status %d\n" ,
ProgramName, editor, WEXITSTATUS(waiter));
goto fatal;
} else if (WIFSIGNALED(waiter)) {
fprintf (stderr,
"%s: \"%s\" killed; signal %d (%score dumped)\n" ,
ProgramName, editor, WTERMSIG(waiter),
WCOREDUMP(waiter) ?"" :"no " );
goto fatal;
} else
break ;
}
(void )signal(SIGHUP, SIG_DFL);
(void )signal(SIGINT, SIG_DFL);
(void )signal(SIGQUIT, SIG_DFL);
if (fstat(t, &statbuf) < 0 ) {
perror("fstat" );
goto fatal;
}
if (utimebuf.modtime == statbuf.st_mtime) {
fprintf (stderr, "%s: no changes made to crontab\n" ,
ProgramName);
goto remove;
}
fprintf (stderr, "%s: installing new crontab\n" , ProgramName);
switch (replace_cmd()) {
case 0 :
break ;
case -1 :
for (;;) {
printf ("Do you want to retry the same edit? " );
fflush(stdout);
q[0 ] = '\0' ;
(void ) fgets(q, sizeof q, stdin);
switch (q[0 ]) {
case 'y' :
case 'Y' :
goto again;
case 'n' :
case 'N' :
goto abandon;
default :
fprintf (stderr, "Enter Y or N\n" );
}
}
case -2 :
abandon:
fprintf (stderr, "%s: edits left in %s\n" ,
ProgramName, Filename);
goto done;
default :
fprintf (stderr, "%s: panic: bad switch() in replace_cmd()\n" ,
ProgramName);
goto fatal;
}
remove:
unlink(Filename);
done:
log_it(RealUser, Pid, "END EDIT" , User);
}