linux crond定时任务源码分析

来源:互联网 发布:中国高速铁路 知乎 编辑:程序博客网 时间:2024/06/08 14:33
   关于crond的介绍和使用,请百度。这里分析下如何实现。   下载源码:http://down1.chinaunix.net/distfiles/vixie-cron-4.1.tar.bz2   该源码中主要由以下两个主要的.c组成:   1:cron.c   2:crontab.c   首先看下crontab.c,这个文件主要实现了crontab命令,来管理配置文件。   main函数:
    intmain(int argc, char *argv[]) {    int exitstatus;    Pid = getpid();    ProgramName = argv[0];    setlocale(LC_ALL, "");#if defined(BSD)    setlinebuf(stderr);#endif    parse_args(argc, argv);     /* sets many globals, opens a file */    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);    /*NOTREACHED*/}
1. 解析函数入参,并设置option变量。2. 根据解析出的option变量,分别调用对应的函数。涉及到的函数实现为列出配置、删除配置、指定配置文件、编辑配置文件。列出配置和删除配置相对简单,分别是读取文件和删除文件。不做分析,接下来主要看下如何编辑配置文件和指定配置文件。编辑文件:
static voidedit_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;    /* Turn off signals. */    (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)    /* ignore the top few comments since we probably put them there.     */    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;    }    /* copy the rest of the crontab (if any) to the temp file.     */    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;    }    /* we still have the file open.  editors will generally rewrite the     * original file rather than renaming/unlinking it and starting a     * new one; even backup files are supposed to be made by copying     * rather than by renaming.  if some editor does not support this,     * then don't use it.  the security problems are more severe if we     * close and reopen the file around the edit.     */    switch (pid = fork()) {    case -1:        perror("fork");        goto fatal;    case 0:        /* child */        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);        /*NOTREACHED*/    default:        /* parent */        break;    }    /* parent */    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");            }        }        /*NOTREACHED*/    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);}
原创粉丝点击