/* Logging routines.
 *
 * (C) 2003-2005 Anope Team
 * Contact us at info@anope.org
 *
 * Please read COPYING and README for further details.
 *
 * Based on the original code of Epona by Lara.
 * Based on the original code of Services by Andy Church. 
 * 
 * $Id: log.c 959 2006-01-25 20:44:50Z rob $ 
 *
 */

#include "services.h"
#include "pseudo.h"

static FILE *logfile;

static int curday = 0;

/*************************************************************************/

static int get_logname(char *name, int count, struct tm *tm)
{

    char timestamp[32];

    if (!tm) {
        time_t t;

        time(&t);
        tm = localtime(&t);
    }

    strftime(timestamp, count, "%Y%m%d", tm);
    snprintf(name, count, "logs/%s.%s", log_filename, timestamp);
    curday = tm->tm_yday;

    return 1;
}

/*************************************************************************/

static void remove_log(void)
{
    time_t t;
    struct tm tm;

    char name[PATH_MAX];

    if (!KeepLogs)
        return;

    time(&t);
    t -= (60 * 60 * 24 * KeepLogs);
    tm = *localtime(&t);

    if (!get_logname(name, sizeof(name), &tm))
        return;
#ifndef _WIN32
    unlink(name);
#else
    DeleteFile(name);
#endif
}

/*************************************************************************/

static void checkday(void)
{
    time_t t;
    struct tm tm;

    time(&t);
    tm = *localtime(&t);

    if (curday != tm.tm_yday) {
        close_log();
        remove_log();
        open_log();
    }
}

/*************************************************************************/

/* Open the log file.  Return -1 if the log file could not be opened, else
 * return 0. */

int open_log(void)
{
    char name[PATH_MAX];

    if (logfile)
        return 0;

    if (!get_logname(name, sizeof(name), NULL))
        return 0;
    logfile = fopen(name, "a");

    if (logfile)
        setbuf(logfile, NULL);
    return logfile != NULL ? 0 : -1;
}

/* Close the log file. */

void close_log(void)
{
    if (!logfile)
        return;
    fclose(logfile);
    logfile = NULL;
}

/*************************************************************************/

/* Log stuff to the log file with a datestamp.  Note that errno is
 * preserved by this routine and log_perror().
 */

void alog(const char *fmt, ...)
{
    va_list args, logargs, consoleargs, logchanargs;
    time_t t;
    struct tm tm;
    char buf[256];
    int errno_save = errno;

    checkday();

    if (!fmt) {
        return;
    }

    va_start(args, fmt);

    /* 64-bit safety:
     *
     * GCC on many 64-bit boxes causes the stack used for the va_list
     * to become unavailable after you use them, so we need to make copies
     * of the va_list for all of our logging targets, otherwise Anope will
     * crash. :(
     *
     *   --nenolod
     */
    VA_COPY(logargs, args);
    VA_COPY(consoleargs, args);
    VA_COPY(logchanargs, args);

    time(&t);
    tm = *localtime(&t);
#if HAVE_GETTIMEOFDAY
    if (debug) {
        char *s;
        struct timeval tv;
        gettimeofday(&tv, NULL);
        strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S", &tm);
        s = buf + strlen(buf);
        s += snprintf(s, sizeof(buf) - (s - buf), ".%06d",
                      (int) tv.tv_usec);
        strftime(s, sizeof(buf) - (s - buf) - 1, " %Y] ", &tm);
    } else {
#endif
        strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S %Y] ", &tm);
#if HAVE_GETTIMEOFDAY
    }
#endif
    if (logfile) {
        fputs(buf, logfile);
        vfprintf(logfile, fmt, logargs);
        fputc('\n', logfile);
    }
    if (nofork) {
        fputs(buf, stderr);
        vfprintf(stderr, fmt, consoleargs);
        fputc('\n', stderr);
    }

    if (LogChannel && logchan && !debug && findchan(LogChannel)) {
        char str[BUFSIZE];
        vsnprintf(str, sizeof(str), fmt, logchanargs);
        privmsg(s_GlobalNoticer, LogChannel, "%s", str);
    }

    va_end(logargs);
    va_end(consoleargs);
    va_end(logchanargs);
    va_end(args);
    errno = errno_save;
}


/* Like alog(), but tack a ": " and a system error message (as returned by
 * strerror()) onto the end.
 */

void log_perror(const char *fmt, ...)
{
    va_list args, logargs, consoleargs;
    time_t t;
    struct tm tm;
    char buf[256];
    int errno_save = errno;

    checkday();

    if (!fmt) {
        return;
    }

    va_start(args, fmt);

    /* 64-bit safety:
     *
     * GCC on many 64-bit boxes causes the stack used for the va_list
     * to become unavailable after you use them, so we need to make copies
     * of the va_list for all of our logging targets, otherwise Anope will
     * crash. :(
     *
     *   --nenolod
     */
    VA_COPY(logargs, args);
    VA_COPY(consoleargs, args);

    time(&t);
    tm = *localtime(&t);
#if HAVE_GETTIMEOFDAY
    if (debug) {
        char *s;
        struct timeval tv;
        gettimeofday(&tv, NULL);
        strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S", &tm);
        s = buf + strlen(buf);
        s += snprintf(s, sizeof(buf) - (s - buf), ".%06d",
                      (int) tv.tv_usec);
        strftime(s, sizeof(buf) - (s - buf) - 1, " %Y] ", &tm);
    } else {
#endif
        strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S %Y] ", &tm);
#if HAVE_GETTIMEOFDAY
    }
#endif
    if (logfile) {
        fputs(buf, logfile);
        vfprintf(logfile, fmt, logargs);
        fprintf(logfile, ": %s\n", strerror(errno_save));
    }
    if (nofork) {
        fputs(buf, stderr);
        vfprintf(stderr, fmt, consoleargs);
        fprintf(stderr, ": %s\n", strerror(errno_save));
    }
    errno = errno_save;

    va_end(logargs);
    va_end(consoleargs);
    va_end(args);
}

/*************************************************************************/

/* We've hit something we can't recover from.  Let people know what
 * happened, then go down.
 */

void fatal(const char *fmt, ...)
{
    va_list args;
    time_t t;
    struct tm tm;
    char buf[256], buf2[4096];

    checkday();

    va_start(args, fmt);
    time(&t);
    tm = *localtime(&t);
#if HAVE_GETTIMEOFDAY
    if (debug) {
        char *s;
        struct timeval tv;
        gettimeofday(&tv, NULL);
        strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S", &tm);
        s = buf + strlen(buf);
        s += snprintf(s, sizeof(buf) - (s - buf), ".%06d",
                      (int) tv.tv_usec);
        strftime(s, sizeof(buf) - (s - buf) - 1, " %Y] ", &tm);
    } else {
#endif
        strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S %Y] ", &tm);
#if HAVE_GETTIMEOFDAY
    }
#endif
    vsnprintf(buf2, sizeof(buf2), fmt, args);
    if (logfile)
        fprintf(logfile, "%sFATAL: %s\n", buf, buf2);
    if (nofork)
        fprintf(stderr, "%sFATAL: %s\n", buf, buf2);
    if (servsock >= 0)
        anope_cmd_global(NULL, "FATAL ERROR!  %s", buf2);

    va_end(args);
    exit(1);
}


/* Same thing, but do it like perror(). */

void fatal_perror(const char *fmt, ...)
{
    va_list args;
    time_t t;
    struct tm tm;
    char buf[256], buf2[4096];
    int errno_save = errno;

    checkday();

    va_start(args, fmt);
    time(&t);
    tm = *localtime(&t);
#if HAVE_GETTIMEOFDAY
    if (debug) {
        char *s;
        struct timeval tv;
        gettimeofday(&tv, NULL);
        strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S", &tm);
        s = buf + strlen(buf);
        s += snprintf(s, sizeof(buf) - (s - buf), ".%06d",
                      (int) tv.tv_usec);
        strftime(s, sizeof(buf) - (s - buf) - 1, " %Y] ", &tm);
    } else {
#endif
        strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S %Y] ", &tm);
#if HAVE_GETTIMEOFDAY
    }
#endif
    vsnprintf(buf2, sizeof(buf2), fmt, args);
    if (logfile)
        fprintf(logfile, "%sFATAL: %s: %s\n", buf, buf2,
                strerror(errno_save));
    if (stderr)
        fprintf(stderr, "%sFATAL: %s: %s\n", buf, buf2,
                strerror(errno_save));
    if (servsock >= 0)
        anope_cmd_global(NULL, "FATAL ERROR!  %s: %s", buf2,
                         strerror(errno_save));
    va_end(args);
    exit(1);
}

/*************************************************************************/

/* Same thing, but do it like perror(). 
 * This is for socket errors. On *nix, it works just like fatal_perror,
 * on Win32, it uses the socket error code and formatting functions.
 */

void fatal_sockerror(const char *fmt, ...)
{
    va_list args;
    time_t t;
    struct tm tm;
    char buf[256], buf2[4096];
    int errno_save = ano_sockgeterr();

    checkday();

    va_start(args, fmt);
    time(&t);
    tm = *localtime(&t);
#if HAVE_GETTIMEOFDAY
    if (debug) {
        char *s;
        struct timeval tv;
        gettimeofday(&tv, NULL);
        strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S", &tm);
        s = buf + strlen(buf);
        s += snprintf(s, sizeof(buf) - (s - buf), ".%06d", tv.tv_usec);
        strftime(s, sizeof(buf) - (s - buf) - 1, " %Y] ", &tm);
    } else {
#endif
        strftime(buf, sizeof(buf) - 1, "[%b %d %H:%M:%S %Y] ", &tm);
#if HAVE_GETTIMEOFDAY
    }
#endif
    vsnprintf(buf2, sizeof(buf2), fmt, args);
    if (logfile)
        fprintf(logfile, "%sFATAL: %s: %s\n", buf, buf2,
                ano_sockstrerror(errno_save));
    if (stderr)
        fprintf(stderr, "%sFATAL: %s: %s\n", buf, buf2,
                ano_sockstrerror(errno_save));
    if (servsock >= 0)
        wallops(NULL, "FATAL ERROR!  %s: %s", buf2, strerror(errno_save));
    exit(1);
}
