static int findNeedRotating(const struct logInfo *log, unsigned logNum, int force)
{
    struct stat sb;
    struct logState *state = NULL;
    struct tm now;

    message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);

    localtime_r(&nowSecs, &now);

    /* Check if parent directory of this log has safe permissions */
    if ((log->flags & LOG_FLAG_SU) == 0 && getuid() == ROOT_UID) {
        char *ld;
        char *logpath = strdup(log->files[logNum]);
        if (logpath == NULL) {
            message_OOM();
            return 1;
        }
        ld = dirname(logpath);
        if (stat(ld, &sb)) {
            /* If parent directory doesn't exist, it's not real error
               (unless nomissingok is specified)
               and rotation is not needed */
            if (errno != ENOENT || (log->flags & LOG_FLAG_MISSINGOK) == 0) {
                message(MESS_ERROR, "stat of %s failed: %s\n", ld,
                        strerror(errno));
                free(logpath);
                return 1;
            }
            free(logpath);
            return 0;
        }
        /* Don't rotate in directories writable by others or group which is not "root"  */
        if ((sb.st_gid != 0 && (sb.st_mode & S_IWGRP)) || (sb.st_mode & S_IWOTH)) {
            message(MESS_ERROR, "skipping \"%s\" because parent directory has insecure permissions"
                    " (It's world writable or writable by group which is not \"root\")"
                    " Set \"su\" directive in config file to tell logrotate which user/group"
                    " should be used for rotation.\n"
                    ,log->files[logNum]);
            free(logpath);
            return 1;
        }
        free(logpath);
    }

    if (lstat(log->files[logNum], &sb)) {
        if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
            message(MESS_DEBUG, "  log %s does not exist -- skipping\n",
                    log->files[logNum]);
            return 0;
        }
        message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
                strerror(errno));
        return 1;
    }

    state = findState(log->files[logNum]);
    if (!state)
        return 1;

    state->doRotate = 0;
    state->sb = sb;
    state->isUsed = 1;

    if ((sb.st_mode & S_IFMT) == S_IFLNK) {
        message(MESS_DEBUG, "  log %s is symbolic link. Rotation of symbolic"
                " links is not allowed to avoid security issues -- skipping.\n",
                log->files[logNum]);
        return 0;
    }

    if (!(log->flags & LOG_FLAG_ALLOWHARDLINK) && sb.st_nlink != 1) {
        message(MESS_DEBUG, "  log %s has multiple (%lu) hard links. Rotation of files"
                " with multiple hard links is not allowed for %s -- skipping.\n",
                log->files[logNum], (unsigned long)sb.st_nlink, log->pattern);
        return 0;
    }

    message(MESS_DEBUG, "  Now: %d-%02d-%02d %02d:%02d\n", 1900 + now.tm_year,
            1 + now.tm_mon, now.tm_mday,
            now.tm_hour, now.tm_min);

    message(MESS_DEBUG, "  Last rotated at %d-%02d-%02d %02d:%02d\n", 1900 + state->lastRotated.tm_year,
            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
            state->lastRotated.tm_hour, state->lastRotated.tm_min);

    if (force) {
        /* user forced rotation of logs from command line */
        state->doRotate = 1;
    }
    else if (log->maxsize && sb.st_size > log->maxsize) {
        state->doRotate = 1;
    }
    else if (log->criterium == ROT_SIZE) {
        state->doRotate = (sb.st_size >= log->threshold);
        if (!state->doRotate) {
            message(MESS_DEBUG, "  log does not need rotating "
                    "(log size is below the 'size' threshold)\n");
        }
    } else if (difftime(mktime(&state->lastRotated), mktime(&now)) > (25 * 3600)) {
        /* 25 hours allows for DST changes as well as geographical moves */
        message(MESS_ERROR,
                "log %s last rotated in the future -- rotation forced\n",
                log->files[logNum]);
        state->doRotate = 1;
    } else if (state->lastRotated.tm_year != now.tm_year ||
            state->lastRotated.tm_mon != now.tm_mon ||
            state->lastRotated.tm_mday != now.tm_mday ||
            state->lastRotated.tm_hour != now.tm_hour) {
        long days;
        switch (log->criterium) {
            case ROT_WEEKLY:
                days = daysElapsed(&now, &state->lastRotated);
                /* rotate if date is advanced by 7+ days (exact time is ignored) */
                state->doRotate = (days >= 7)
                    /* ... or if we have not yet rotated today */
                    || (days >= 1
                            /* ... and the selected weekday is today */
                            && (unsigned)now.tm_wday == log->weekday);
                if (!state->doRotate) {
                    message(MESS_DEBUG, "  log does not need rotating "
                            "(log has been rotated at %d-%02d-%02d %02d:%02d, "
                            "which is less than a week ago)\n", 1900 + state->lastRotated.tm_year,
                            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
                            state->lastRotated.tm_hour, state->lastRotated.tm_min);
                }
                break;
            case ROT_HOURLY:
                state->doRotate = ((now.tm_hour != state->lastRotated.tm_hour) ||
                        (now.tm_mday != state->lastRotated.tm_mday) ||
                        (now.tm_mon != state->lastRotated.tm_mon) ||
                        (now.tm_year != state->lastRotated.tm_year));
                if (!state->doRotate) {
                    message(MESS_DEBUG, "  log does not need rotating "
                            "(log has been rotated at %d-%02d-%02d %02d:%02d, "
                            "which is less than an hour ago)\n", 1900 + state->lastRotated.tm_year,
                            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
                            state->lastRotated.tm_hour, state->lastRotated.tm_min);
                }
                break;
            case ROT_DAYS:
                state->doRotate = ((now.tm_mday != state->lastRotated.tm_mday) ||
                        (now.tm_mon != state->lastRotated.tm_mon) ||
                        (now.tm_year != state->lastRotated.tm_year));
                if (!state->doRotate) {
                    message(MESS_DEBUG, "  log does not need rotating "
                            "(log has been rotated at %d-%02d-%02d %02d:%02d, "
                            "which is less than a day ago)\n", 1900 + state->lastRotated.tm_year,
                            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
                            state->lastRotated.tm_hour, state->lastRotated.tm_min);
                }
                break;
            case ROT_MONTHLY:
                /* rotate if the logs haven't been rotated this month or
                   this year */
                state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
                        (now.tm_year != state->lastRotated.tm_year));
                if (!state->doRotate) {
                    message(MESS_DEBUG, "  log does not need rotating "
                            "(log has been rotated at %d-%02d-%02d %02d:%02d, "
                            "which is less than a month ago)\n", 1900 + state->lastRotated.tm_year,
                            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
                            state->lastRotated.tm_hour, state->lastRotated.tm_min);
                }
                break;
            case ROT_YEARLY:
                /* rotate if the logs haven't been rotated this year */
                state->doRotate = (now.tm_year != state->lastRotated.tm_year);
                if (!state->doRotate) {
                    message(MESS_DEBUG, "  log does not need rotating "
                            "(log has been rotated at %d-%02d-%02d %02d:%02d, "
                            "which is less than a year ago)\n", 1900 + state->lastRotated.tm_year,
                            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
                            state->lastRotated.tm_hour, state->lastRotated.tm_min);
                }
                break;
            case ROT_SIZE:
            default:
                /* ack! */
                state->doRotate = 0;
                break;
        }
        if (log->minsize && sb.st_size < log->minsize) {
            state->doRotate = 0;
            message(MESS_DEBUG, "  log does not need rotating "
                    "('minsize' directive is used and the log "
                    "size is smaller than the minsize value)\n");
        }
        if (log->rotateMinAge && log->rotateMinAge * DAY_SECONDS >= difftime(nowSecs, sb.st_mtime)) {
            state->doRotate = 0;
            message(MESS_DEBUG, "  log does not need rotating "
                    "('minage' directive is used and the log "
                    "age is smaller than the minage days)\n");
        }
    }
    else if (!state->doRotate) {
        message(MESS_DEBUG, "  log does not need rotating "
                "(log has already been rotated)\n");
    }

    /* The notifempty flag overrides the normal criteria */
    if (state->doRotate && !(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size) {
        state->doRotate = 0;
        message(MESS_DEBUG, "  log does not need rotating "
                "(log is empty)\n");
    }

    if (state->doRotate) {
        message(MESS_DEBUG, "  log needs rotating\n");
    }

    return 0;
}
static int findNeedRotating(const struct logInfo *log, unsigned logNum, int force)
{
    struct stat sb;
    struct logState *state = NULL;
    struct tm now;

    message(MESS_DEBUG, "considering log %s\n", log->files[logNum]);

    localtime_r(&nowSecs, &now);

     
    if ((log->flags & LOG_FLAG_SU) == 0 && getuid() == ROOT_UID) {
        char *ld;
        char *logpath = strdup(log->files[logNum]);
        if (logpath == NULL) {
            message_OOM();
            return 1;
        }
        ld = dirname(logpath);
        if (stat(ld, &sb)) {
             
            if (errno != ENOENT || (log->flags & LOG_FLAG_MISSINGOK) == 0) {
                message(MESS_ERROR, "stat of %s failed: %s\n", ld,
                        strerror(errno));
                free(logpath);
                return 1;
            }
            free(logpath);
            return 0;
        }
         
        if ((sb.st_gid != 0 && (sb.st_mode & S_IWGRP)) || (sb.st_mode & S_IWOTH)) {
            message(MESS_ERROR, "skipping \"%s\" because parent directory has insecure permissions"
                    " (It's world writable or writable by group which is not \"root\")"
                    " Set \"su\" directive in config file to tell logrotate which user/group"
                    " should be used for rotation.\n"
                    ,log->files[logNum]);
            free(logpath);
            return 1;
        }
        free(logpath);
    }

    if (lstat(log->files[logNum], &sb)) {
        if ((log->flags & LOG_FLAG_MISSINGOK) && (errno == ENOENT)) {
            message(MESS_DEBUG, "  log %s does not exist -- skipping\n",
                    log->files[logNum]);
            return 0;
        }
        message(MESS_ERROR, "stat of %s failed: %s\n", log->files[logNum],
                strerror(errno));
        return 1;
    }

    state = findState(log->files[logNum]);
    if (!state)
        return 1;

    state->doRotate = 0;
    state->sb = sb;
    state->isUsed = 1;

    if ((sb.st_mode & S_IFMT) == S_IFLNK) {
        message(MESS_DEBUG, "  log %s is symbolic link. Rotation of symbolic"
                " links is not allowed to avoid security issues -- skipping.\n",
                log->files[logNum]);
        return 0;
    }

    if (!(log->flags & LOG_FLAG_ALLOWHARDLINK) && sb.st_nlink != 1) {
        message(MESS_DEBUG, "  log %s has multiple (%lu) hard links. Rotation of files"
                " with multiple hard links is not allowed for %s -- skipping.\n",
                log->files[logNum], (unsigned long)sb.st_nlink, log->pattern);
        return 0;
    }

    message(MESS_DEBUG, "  Now: %d-%02d-%02d %02d:%02d\n", 1900 + now.tm_year,
            1 + now.tm_mon, now.tm_mday,
            now.tm_hour, now.tm_min);

    message(MESS_DEBUG, "  Last rotated at %d-%02d-%02d %02d:%02d\n", 1900 + state->lastRotated.tm_year,
            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
            state->lastRotated.tm_hour, state->lastRotated.tm_min);

    if (force) {
         
        state->doRotate = 1;
    }
    else if (log->maxsize && sb.st_size > log->maxsize) {
        state->doRotate = 1;
    }
    else if (log->criterium == ROT_SIZE) {
        state->doRotate = (sb.st_size >= log->threshold);
        if (!state->doRotate) {
            message(MESS_DEBUG, "  log does not need rotating "
                    "(log size is below the 'size' threshold)\n");
        }
    } else if (difftime(mktime(&state->lastRotated), mktime(&now)) > (25 * 3600)) {
         
        message(MESS_ERROR,
                "log %s last rotated in the future -- rotation forced\n",
                log->files[logNum]);
        state->doRotate = 1;
    } else if (state->lastRotated.tm_year != now.tm_year ||
            state->lastRotated.tm_mon != now.tm_mon ||
            state->lastRotated.tm_mday != now.tm_mday ||
            state->lastRotated.tm_hour != now.tm_hour) {
        long days;
        switch (log->criterium) {
            case ROT_WEEKLY:
                days = daysElapsed(&now, &state->lastRotated);
                 
                state->doRotate = (days >= 7)
                     
                    || (days >= 1
                             
                            && (unsigned)now.tm_wday == log->weekday);
                if (!state->doRotate) {
                    message(MESS_DEBUG, "  log does not need rotating "
                            "(log has been rotated at %d-%02d-%02d %02d:%02d, "
                            "which is less than a week ago)\n", 1900 + state->lastRotated.tm_year,
                            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
                            state->lastRotated.tm_hour, state->lastRotated.tm_min);
                }
                break;
            case ROT_HOURLY:
                state->doRotate = ((now.tm_hour != state->lastRotated.tm_hour) ||
                        (now.tm_mday != state->lastRotated.tm_mday) ||
                        (now.tm_mon != state->lastRotated.tm_mon) ||
                        (now.tm_year != state->lastRotated.tm_year));
                if (!state->doRotate) {
                    message(MESS_DEBUG, "  log does not need rotating "
                            "(log has been rotated at %d-%02d-%02d %02d:%02d, "
                            "which is less than an hour ago)\n", 1900 + state->lastRotated.tm_year,
                            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
                            state->lastRotated.tm_hour, state->lastRotated.tm_min);
                }
                break;
            case ROT_DAYS:
                state->doRotate = ((now.tm_mday != state->lastRotated.tm_mday) ||
                        (now.tm_mon != state->lastRotated.tm_mon) ||
                        (now.tm_year != state->lastRotated.tm_year));
                if (!state->doRotate) {
                    message(MESS_DEBUG, "  log does not need rotating "
                            "(log has been rotated at %d-%02d-%02d %02d:%02d, "
                            "which is less than a day ago)\n", 1900 + state->lastRotated.tm_year,
                            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
                            state->lastRotated.tm_hour, state->lastRotated.tm_min);
                }
                break;
            case ROT_MONTHLY:
                 
                state->doRotate = ((now.tm_mon != state->lastRotated.tm_mon) ||
                        (now.tm_year != state->lastRotated.tm_year));
                if (!state->doRotate) {
                    message(MESS_DEBUG, "  log does not need rotating "
                            "(log has been rotated at %d-%02d-%02d %02d:%02d, "
                            "which is less than a month ago)\n", 1900 + state->lastRotated.tm_year,
                            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
                            state->lastRotated.tm_hour, state->lastRotated.tm_min);
                }
                break;
            case ROT_YEARLY:
                 
                state->doRotate = (now.tm_year != state->lastRotated.tm_year);
                if (!state->doRotate) {
                    message(MESS_DEBUG, "  log does not need rotating "
                            "(log has been rotated at %d-%02d-%02d %02d:%02d, "
                            "which is less than a year ago)\n", 1900 + state->lastRotated.tm_year,
                            1 + state->lastRotated.tm_mon, state->lastRotated.tm_mday,
                            state->lastRotated.tm_hour, state->lastRotated.tm_min);
                }
                break;
            case ROT_SIZE:
            default:
                 
                state->doRotate = 0;
                break;
        }
        if (log->minsize && sb.st_size < log->minsize) {
            state->doRotate = 0;
            message(MESS_DEBUG, "  log does not need rotating "
                    "('minsize' directive is used and the log "
                    "size is smaller than the minsize value)\n");
        }
        if (log->rotateMinAge && log->rotateMinAge * DAY_SECONDS >= difftime(nowSecs, sb.st_mtime)) {
            state->doRotate = 0;
            message(MESS_DEBUG, "  log does not need rotating "
                    "('minage' directive is used and the log "
                    "age is smaller than the minage days)\n");
        }
    }
    else if (!state->doRotate) {
        message(MESS_DEBUG, "  log does not need rotating "
                "(log has already been rotated)\n");
    }

     
    if (state->doRotate && !(log->flags & LOG_FLAG_IFEMPTY) && !sb.st_size) {
        state->doRotate = 0;
        message(MESS_DEBUG, "  log does not need rotating "
                "(log is empty)\n");
    }

    if (state->doRotate) {
        message(MESS_DEBUG, "  log needs rotating\n");
    }

    return 0;
}