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

  POPular -- A POP3 server and proxy for large mail systems

  $Id: xlog.c,v 1.21 2001/05/17 20:17:23 sqrt Exp $

  http://www.remote.org/jochen/mail/popular/

******************************************************************************

  Copyright (C) 1999-2001  Jochen Topf <jochen@remote.org>

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA

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

#if HAVE_CONFIG_H
# include <config.h>
#endif

#include "popular.h"


static char xhostname[256];
static char xprgname[256];
static char xid[256];

/* in the beginning the file descriptor for logging is 2 for stdout */
static int xfd = 2;


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

  xlog_writeln()

  Internal function

  This function is used by the logging function, it writes a line to the
  logfile. Tabs, newlines and carriage returns are printed as space, non-
  printable characters are printed as a dot (.). A single newline is
  included at the end.

*****************************************************************************/
void
xlog_writeln(char *string)
{
  char buf[strlen(string)*4+2];
  unsigned char *s, *t;

  /* no logging if there is no fd to log to */
  if (xfd == -1) return;

  for (s=string, t=buf; *s; s++, t++) {
    if      (*s == '\n')  { *t++ = '\\'; *t = 'n'; }
    else if (*s == '\r')  { *t++ = '\\'; *t = 'r'; }
    else if (*s == '\t')  { *t++ = '\\'; *t = 't'; }
    else if (*s == '\"')  { *t++ = '\\'; *t = '\"'; }
    else if (*s == '\\')  { *t++ = '\\'; *t = '\\'; }
    else if (isprint(*s)) { *t = *s; }
    else {
      *t++ = '\\';
      *t++ = 'x';
      snprintf(t++, 3, "%02x", (unsigned int) *s);
    }
  }
  *t++ = '\n';
  *t   = '\0';
  (void) rel_write(xfd, buf, strlen(buf));
}


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

  xlog_print_level()
 
*****************************************************************************/
const char *
xlog_print_level(xlog_level_t level)
{
  switch (level) {
    case xlog_dbg: return("DBG");
    case xlog_inf: return("INF");
    case xlog_err: return("ERR");
    case xlog_adm: return("ADM");
    case xlog_sos: return("SOS");
    case xlog_bug: return("BUG");
  }
  return("BUG");
}


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

  xlog_printf()

  Write some information to the logfile and exit. Long strings will be
  truncated. ('...' is printed at the end to signal truncation.) The following
  characters are printed like escpaed: \r, \n, \t, \\, \". Other nonprintable
  characters are printedas hexadecimal: \xXX.

  Log format:
  <date> <time> <hostname> <prgname> <pid> <idstring> <level> <code> <text>

  Date and time use the ISO format (yyyymmdd and hhmmss), the hostname is
  printed without domain. The level can be one of: DEBUG, INFO, ERR, ADMIN,
  SOS, BUG. The code is a 4 digit unique hex number.

*****************************************************************************/
void
xlog_printf(xlog_level_t level, int code, const char *format, ...)
{
  char buf[MAXBUF * 2];
  va_list args;
  time_t now = time(NULL);
  struct tm *t = localtime(&now);
  int r, rr;

  r = snprintf(buf, sizeof(buf), "%04d%02d%02d %02d%02d%02d %s %s %d %s %s %04x ",
	t->tm_year+1900, t->tm_mon+1, t->tm_mday,
	t->tm_hour, t->tm_min, t->tm_sec,
	xhostname, xprgname, getpid(), xid, xlog_print_level(level), code);

  if (r < 0 || r >= sizeof(buf)) {
    buf[sizeof(buf)-2] = '.';
    buf[sizeof(buf)-3] = '.';
    buf[sizeof(buf)-4] = '.';
    goto done;
  }

  va_start(args, format);
  rr = vsnprintf(buf+r, sizeof(buf)-r, format, args);
  va_end(args);

  if (rr < 0 || rr >= sizeof(buf)-r) {
    buf[sizeof(buf)-2] = '.';
    buf[sizeof(buf)-3] = '.';
    buf[sizeof(buf)-4] = '.';
  }

done:
  xlog_writeln(buf);
}


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

  xlog_set_id()

  Set ID for logging

*****************************************************************************/
void
xlog_set_id(const char *id)
{
  (void) strlcpy(xid, id, sizeof(xid));
}


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

  xlog_init()

  Save program and host name for logging.

*****************************************************************************/
void
xlog_init(const char *prgname)
{
  char *p;

  /* set ID to "-", it is set later with xlog_set_id() to the right value */
  xid[0] = '-';
  xid[1] = '\0';

  (void) strlcpy(xprgname, prgname, sizeof(xprgname));

  /* Get hostname for logging and drop domain */
  (void) gethostname(xhostname, sizeof(xhostname));
  xhostname[sizeof(xhostname)-1] = '\0';
  p = strchr(xhostname, '.');
  if (p) *p = '\0';
}


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

  xlog_open()

  Save program and host name for logging and open logfile

*****************************************************************************/
int
xlog_open(const char *prgname, const char *logfilename, mode_t mode)
{
  xlog_init(prgname);

  /* Open logfile. Return code is not checked. If logging doesn't work
     everything else is still usable. The caller might want to check this. */
  xfd = open(logfilename, O_WRONLY | O_APPEND | O_CREAT, mode);
  return xfd;
}


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

  xlog_close()

  Close log file if it was open.

*****************************************************************************/
void
xlog_close()
{
  if (xfd != -1) {
    (void) close(xfd);
    xfd = -1;
  }
}


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

  xlog_reopen()

  Reopen the log file. If opening the new file failed, the old file will
  remain open.

*****************************************************************************/
int
xlog_reopen(const char *logfilename, mode_t mode)
{
  int nfd;

  /* XLOG-DOC:INF:000a:log_reopen
   * The logfile will be reopened now. This should be the last message
   * of the parent process of the server to the log file. (See 000c for
   * the message in the new log file) */
  xlog_printf(xlog_inf, 0x000a, "log_reopen");

  /* Open logfile. Return code is not checked. If logging doesn't work
     everything else is still usable. The caller might want to check this. */
  nfd = open(logfilename, O_WRONLY | O_APPEND | O_CREAT, mode);

  if (nfd < 0) {
    /* XLOG-DOC:SOS:000b:log_reopen_failed
     * Reopening of the log file failed for some reason. The old file will
     * be kept open. */
    xlog_printf(xlog_sos, 0x000b, "log_reopen_failed file='%s' errno=%d errmsg='%s'", logfilename, errno, strerror(errno));
    return -1;
  }

  xlog_close();
  xfd = nfd;

  /* XLOG-DOC:INF:000c:log_reopened
   * This is the first message in the reopened log file.
   * (See also: 000a). */
  xlog_printf(xlog_inf, 0x000c, "log_reopened");

  return xfd;
}


/** THE END *****************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1