linux crond定时任务源码分析

   关于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);     /* 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 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;

    /* 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);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值