/*
 * $Header: /san01/cvs/ashpool/csrc/continuity/mecha/Attic/log.c,v 1.8 2004/04/30 13:41:56 aleigh Exp $ 
 */

/*
 * Copyright (c) 1994, 2004 Alex Leigh, All Rights Reserved
 * 
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 * 
 * Disclosure, copying, distribution, or transmission of of this code without
 * express permission is prohibited.
 */


#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <stdarg.h>

#include "mecha.h"

#define _LOG_ROTATECOUNT 5
#define _LOG_ROTATETHRESH 100

static FILE *logGoutfh;

/* Should we fclose() the filehandle? 0 for stdout */
static int logGshould_close=0;

static int logGwritten=0;

/* The output filename if not stdout */
static char *logGoutfn=NULL;
    
static pthread_mutex_t logLrotate=PTHREAD_MUTEX_INITIALIZER;

/* The size all the log files will be allowed to grow to, in
 * bytes. Each individual log file will then rotate at around
 * (logGrotatesize/_LOG_ROTATECOUNT)
 */
static size_t logGrotatesize=0;

static void logFrotate_check(void);
void logFoutputfh_set(FILE *fh, int close_flag);

const char *messages[] = {
    "Unknown Internal Error",
    "Interrupt handler caught signal, Exiting.",
    "Could not open module configuration file. Exiting.",
    "Calling 3rd party dispatcher. [d: %s]",
    "Unable to map 3rd party dispatcher. [d: %s]",
    "netFipv4_listen_tcp failed. [e: %d]",
    /* 5 */
    "Starting IPv4 listen loop. [tid: 0x%x] [handler: 0x%x]",
    "lsnFipv4_listen_loop: accept() took EINTR.",
    "lsnFipv4_listen_loop: accept: %s",
    "Thread pool full. Closing connection.",
    "Thread pool created. [threads: %d] [queue: %d] [blocking: %d]",
    /* 10 */
    "dlFread_entry: sscanf() returned other than 2. [s: %d] [e: %d]",
    "stat() failed. [f: %s] [e: %d]",
    "Internal Error: %s",
    "open() failed. [f: %s] [e: %d]",
    "Memory exhausted: %s",
    /* 15 */
    "mmap() failed. [e: %d] %s",
    "read() failed. [e: %d] %s",
    "munmap() failed. [e: %d] %s",
    "write() failed. [e: %d] %s"
};

static int logFreopen(void);

void logFinit(void) {
  logGoutfh=stdout;
}

static void logFrotate_check(void) {
    size_t filesize;
    static int working=0;

    if(logGwritten<_LOG_ROTATETHRESH || working==1) {
        return;
    }

    if(logGrotatesize==0||logGoutfn==NULL) {
        return;
    }

    pthread_mutex_lock(&logLrotate);

        /* The point of this is to just help prevent other threads
         * from backing up and trying to acquire the lock when we are clearly
         * on the case.
         */
    working=1;

    filesize=fleFget_size(logGoutfn);
    
    if(filesize>(logGrotatesize/_LOG_ROTATECOUNT)) {
        fleFrotate(_LOG_ROTATECOUNT,logGoutfn);
        logFreopen();
        logGwritten=0;
    }

    logGwritten=0;
    
    pthread_mutex_unlock(&logLrotate);

    working=0;
}

void logFset_rotatesize(size_t size) {
    logGrotatesize=size;
}

static int logFreopen(void) {
    FILE *fh;
    
    assert(logGoutfn!=NULL);

    fh=fopen(logGoutfn,"a");
    if(fh==NULL) {
        return -1;
    }

    logFoutputfh_set(fh,logGshould_close);
    
    return 0;
}

/*
 * MT UNSAFE
 */
int logFset_outputfile(const char *filename) {
    FILE *nh;

    assert(filename!=NULL);
    
    nh=fopen(filename,"a");
    if(nh==NULL) {
        return -1;
    }

    logFoutputfh_set(nh,logGshould_close);
    logGoutfn=strFcopy(filename);
    
    assert(logGoutfn!=NULL);
    
    logGshould_close=1;

    return 0;
}

/*
 * MT Safe; result is volitile
 */
FILE *logFoutputfh_get(void) {
    return logGoutfh;
}

/*
 * MT Unsafe
 */
void logFoutputfh_set(FILE *fh, int close_flag) {
    FILE *oldfh = logGoutfh;

    logGoutfh=fh;

        /* We do it this way to minimize the race */
    if(close_flag==1) {
        fclose(oldfh);
    }
}

/*
 * MT Safe
 */
void logFmsgid(int level, const char *module, int msg_id, ...)
{
    va_list ap;
    const char *m = messages[msg_id];

    va_start(ap, msg_id);

    logGwritten++;
    logFrotate_check();
    
    switch (level) {
        case 0:
            fprintf(logGoutfh,"%s/  OK: %s-%05d ", utlFlocal_time(), module, msg_id);
            vfprintf(logGoutfh,m, ap);
            fprintf(logGoutfh,"\n");
            fflush(logGoutfh);
            break;
        case 1:
            fprintf(logGoutfh,"%s/WARN: %s-%05d ", utlFlocal_time(), module, msg_id);
            vfprintf(logGoutfh,m, ap);
            fprintf(logGoutfh,"\n");
            fflush(logGoutfh);
            break;
        case 2:
            fprintf(logGoutfh,"%s/ ERR: %s-%05d ", utlFlocal_time(), module, msg_id);
            vfprintf(logGoutfh,m, ap);
            fprintf(logGoutfh,"\n");
            fflush(logGoutfh);
            break;
        case 3:
            fprintf(logGoutfh,"%s/DIAG: %s-%05d ", utlFlocal_time(), module, msg_id);
            vfprintf(logGoutfh,m, ap);
            fprintf(logGoutfh,"\n");
            fflush(logGoutfh);
            break;
    }
}

/*
 * MT Safe
 */
void logF_msg(const char *func, int level, const char *m, ...)
{
    va_list ap;

    logGwritten++;
    logFrotate_check();
    
    va_start(ap, m);
    
    switch (level) {
    case 0:
        fprintf(logGoutfh, "%s/  OK: %s: ", utlFlocal_time(), func);
        vfprintf(logGoutfh,m, ap);
        fprintf(logGoutfh,"\n");
        fflush(logGoutfh);
        break;
        
    case 1:
        fprintf(logGoutfh,"%s/WARN: %s: ", utlFlocal_time(), func);
        vfprintf(logGoutfh,m, ap);
        fprintf(logGoutfh,"\n");
        fflush(logGoutfh);
        break;
        
    case 2:
        fprintf(logGoutfh,"%s/ ERR: %s: ", utlFlocal_time(), func);
        vfprintf(logGoutfh,m, ap);
        fprintf(logGoutfh,"\n");
        fflush(logGoutfh);
        break;
        
    case 3:
        fprintf(logGoutfh,"%s/DIAG: %s: ", utlFlocal_time(), func);
        vfprintf(logGoutfh,m, ap);
        fprintf(logGoutfh,"\n");
        fflush(logGoutfh);
        break;
    }
}
