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

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

  $Id: pstatus.c,v 1.22 2001/05/24 21:15:44 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"


#define CLEARSCREEN "\033[H\033[2J"
#define CURSORHOME  "\033[H"


/* program name for error messages */
static char *prgname;

int slot_min, slot_max, active;


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

  print_proxy_sessions()

  Pretty print a list of proxy sessions.

*****************************************************************************/
void
print_proxy_sessions(const struct shmem_proxy *sp)
{
  int n;
  char ipbuf_local[16];
  char ipbuf_remote[16];
  struct proxy_session *sessions = sp->session;
  time_t current_time=time(NULL), timediff1, timediff2;
  char mailboxbuf[23];
  struct tm tc, *t;

  printf("+--------------------------------------------------------------------------------------------------------------------------------+--------------+--------------+\n");
  printf("|PROXY SESSIONS                                                                                                                  |%14s|%4d/%4d/%4d|\n", sec2prettytime(current_time - sp->starttime), sp->used_sessions, sp->n_sessions, sp->max_sessions);
  printf("+----+-----+-----+--------+--------+---------------+-----+--------+---------------+-----+----------------+----------+------------+---------+----+----+---------+\n");
  printf("|Num |  Pid|State|StateTim|RunTime |Local IP       | Port|VirtServ|Remote IP      | Port|Username        |Backend   |Mailbox               | Bytes in|Bytes out|\n");
  printf("+----+-----+-----+--------+--------+---------------+-----+--------+---------------+-----+----------------+----------+----------------------+---------+---------+\n");
  for (n=slot_min; n < min(sp->n_sessions, slot_max+1); n++) {
    struct session_state_info *ssi = find_session_state_info(sessions[n].state, NULL);
    char *statedesc = (ssi) ? ssi->name : "?????";

    if (sessions[n].state == sstFree) {
      if (active) continue;
      timediff1 = 0;
      timediff2 = 0;
      ipbuf_local[0] = 0;
      ipbuf_remote[0] = 0;
    } else {
      timediff1 = current_time - sessions[n].statetime;
      timediff2 = current_time - sessions[n].starttime;
      (void) strlcpy(ipbuf_local,  inet_ntoa(sessions[n].sin_client_local.sin_addr),  16);
      (void) strlcpy(ipbuf_remote, inet_ntoa(sessions[n].sin_client_remote.sin_addr), 16);
    }
    t = gmtime(&timediff1);
    tc = *t;
    t = gmtime(&timediff2);

    strlcpy(mailboxbuf, sessions[n].mailbox, sizeof(mailboxbuf));
    printf("|%04d|%5d|%-5s|%02d:%02d:%02d|%02d:%02d:%02d|%-15s|%5u|%-8s|%-15s|%5u|%-16s|%-10s|%-22s|%9lu|%9lu|\n",
	   n,
	   (int)sessions[n].pid,
           statedesc,
	   tc.tm_hour, tc.tm_min, tc.tm_sec,
	   t->tm_hour, t->tm_min, t->tm_sec,
	   ipbuf_local,
           ntohs(sessions[n].sin_client_local.sin_port),
	   sessions[n].vs.id,
	   ipbuf_remote,
           ntohs(sessions[n].sin_client_remote.sin_port),
	   sessions[n].username,
           sessions[n].backend.id,
	   mailboxbuf,
	   sessions[n].bytesin,
	   sessions[n].bytesout
          );
  }
  printf("+----+-----+-----+--------+--------+---------------+-----+--------+---------------+-----+----------------+----------+----------------------+---------+---------+\n\n");
}


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

  print_proxy_stat()

  Pretty print general proxy statistics.

*****************************************************************************/
void
print_proxy_stat(const struct shmem_proxy *sp)
{
  int n;
  long sum=0;

  printf("+----------------------------------+\n");
  printf("|STATISTICS                        |\n");
  printf("+---------------------+------------+\n");
  printf("|Connections          | %10lu |\n", sp->stat.connections);
  printf("|Bytes In             | %10lu |\n", sp->stat.bytesin);
  printf("|Bytes Out            | %10lu |\n", sp->stat.bytesout);
  printf("|Mailcheck time (msec)| %10lu |\n", sp->stat.mailcheck_time);
  for (n=0; n < MAILCHECK_MAX_REQUESTS; n++) {
    sum += sp->stat.mailcheck_retry[n];
    printf("|Mailcheck retries %d  | %10lu |\n", n, sp->stat.mailcheck_retry[n]);
  }
  printf("|Mailcheck timeouts   | %10lu |\n", sp->stat.mailcheck_retry[MAILCHECK_MAX_REQUESTS]);
  printf("|Mailcheck requests   | %10lu |\n", sum);
  if (sum > 0) {
    printf("|Avg mailcheck time   | %10lu |\n", sp->stat.mailcheck_time / sum);
  }
  printf("+---------------------+------------+\n");
}


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

  print_proxy()

  Pretty print all proxy state.

*****************************************************************************/
void
print_proxy(const struct shmem_proxy *sp)
{
  printf("Type: %s  Version: %d\n\n", sp->type, sp->version);
  print_proxy_sessions(sp);
  print_proxy_stat(sp);
}


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

  print_backend_sessions()

  Pretty print list of backend sessions.

*****************************************************************************/
void
print_backend_sessions(const struct shmem_backend *ss)
{
  int n;
  char ipbuf[16];
  struct backend_session *sessions = ss->session;
  time_t current_time=time(NULL), timediff1, timediff2;
  struct tm tc, *t;
  char mailboxbuf[41];

  printf("+----------------------------------------------------------------------------------------------------------------------+--------------+--------------+\n");
  printf("|BACKEND SESSIONS                                                                                                      |%14s|%4d/%4d/%4d|\n", sec2prettytime(current_time - ss->starttime), ss->used_sessions, ss->n_sessions, ss->max_sessions);
  printf("+----+-----+-----+--------+--------+---------------+-----+------------------------+-----+----+----+----+----+----------+--------------+--------------+\n");
  printf("|Num |  Pid|State|StateTim|RunTime |Remote IP      | Port|Session Id              |Flags|MNum|MNew|MRed|MDel|Mailbox                                 |\n");
  printf("+----+-----+-----+--------+--------+---------------+-----+------------------------+-----+----+----+----+----+----------------------------------------+\n");
  for (n=slot_min; n < min(ss->n_sessions, slot_max+1); n++) {
    struct session_state_info *ssi = find_session_state_info(sessions[n].state, NULL);
    char *statedesc = (ssi) ? ssi->name : "?????";

    if (sessions[n].state == sstFree) {
      if (active) continue;
      timediff1 = 0;
      timediff2 = 0;
      ipbuf[0] = 0;
    } else {
      timediff1 = current_time - sessions[n].statetime;
      timediff2 = current_time - sessions[n].starttime;
      (void) strlcpy(ipbuf, inet_ntoa(sessions[n].sin_proxy.sin_addr),  16);
    }
    t = gmtime(&timediff1);
    tc = *t;
    t = gmtime(&timediff2);
    strlcpy(mailboxbuf, sessions[n].mailbox, sizeof(mailboxbuf));
    printf("|%04d|%5d|%-5s|%02d:%02d:%02d|%02d:%02d:%02d|%-15s|%5u|%-24s|%-5s|%4lu|%4lu|%4lu|%4lu|%-40s|\n",
	   n,
           (int)sessions[n].pid,
           statedesc,
	   tc.tm_hour, tc.tm_min, tc.tm_sec,
	   t->tm_hour, t->tm_min, t->tm_sec,
	   ipbuf,
           ntohs(sessions[n].sin_proxy.sin_port),
           sessions[n].id,
           sessions[n].flags,
	   sessions[n].msgnum, sessions[n].msgnew, sessions[n].msgread, sessions[n].msgdel,
           mailboxbuf
          );
  }
  printf("+----+-----+-----+--------+--------+---------------+-----+------------------------+-----+----+----+----+----+----------------------------------------+\n\n");
}


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

  print_backend_stat()

  Pretty print all backend statistics.

*****************************************************************************/
void
print_backend_stat(const struct shmem_backend *ss)
{
  printf("+----------------------------------------------+\n");
  printf("|STATISTICS                                    |\n");
  printf("+---------------------------------+------------+\n");
  printf("|Connections                      | %10lu |\n", ss->stat.connections);
  printf("|Messages Read                    | %10lu |\n", ss->stat.msgread);
  printf("|Messages Deleted                 | %10lu |\n", ss->stat.msgdel);
  printf("|Connections properly closed      | %10lu |\n", ss->stat.connquit);
  printf("|Connections not properly closed  | %10lu |\n", ss->stat.connbroken);
  printf("+---------------------------------+------------+\n");
}


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

  print_backend()

  Pretty print all backend state.

*****************************************************************************/
void
print_backend(const struct shmem_backend *ss)
{
  printf("Type: %s  Version: %d\n\n", ss->type, ss->version);
  print_backend_sessions(ss);
  print_backend_stat(ss);
}


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

  copy_shmem()

  Copy content of shared memory into buffer.

*****************************************************************************/
void
copy_shmem(int fd, void *p1, const void *p2, size_t size)
{
  memcpy(p1, p2, size);
}


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

  print_usage()

*****************************************************************************/
void
print_usage(void)
{
  printf("Usage: %s [OPTION] ...\n", prgname);
  printf("Show status of running pproxy or pserv.\n");
  printf("Options:\n");
  printf("  -f, --file=FILE      read status from FILE\n");
  printf("  -p, --program=PRG    read status from program PRG\n");
  printf("  -l, --loop=TIME      loop every TIME seconds (default: 1)\n");
  printf("  -r, --range=N-M      range of slots to print (default: all)\n");
  printf("  -a, --active         print only active sessions\n");
  printf("      --help           print this help message\n");
  printf("      --version        print version information\n");
  printf("\n");
}


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

  main()

*****************************************************************************/
int
main(int argc, char *argv[])
{
  int statusfd, c;
  char statefile[MAXBUF];
  struct shmem_desc *sd;
  struct stat statbuf;
  caddr_t mp;
  int last=0;
  int loop=-1;

  static struct option long_options[] = {
    { "help",          no_argument,       NULL, 0 },
    { "version",       no_argument,       NULL, 0 },
    { "active",        no_argument,       NULL, 'a' },
    { "file",          required_argument, NULL, 'f' },
    { "program",       required_argument, NULL, 'p' },
    { "loop",          optional_argument, NULL, 'l' },
    { "range",         required_argument, NULL, 'r' },
    { NULL,            0,                 NULL, 0 }
  };

  prgname = argv[0];            /* program name for error messages */

  statefile[0] = '\0';

  active = 0;

  slot_min = 0;
  slot_max = 10000;	/* in every case this should be bigger then the
			   largest number of slots */


  /***************************************************************************
    This program should not be run as root and not be setuid or setgid.
  ***************************************************************************/
  if (!getuid() || !geteuid()) {
    fprintf(stderr, "%s: don't start this program as `root'.\n", prgname);
    exit(RCODE_CMDLINE);
  }

  if (getuid() != geteuid() || getgid() != getegid()) {
    fprintf(stderr, "%s: don't make this program setuid or setgid.\n", prgname);
    exit(RCODE_CMDLINE);
  }


  /***************************************************************************
    Read command line options.
  ***************************************************************************/
  while (1) {
    int option_index = 0;
    c = getopt_long(argc, argv, "af:p:r:l::c", long_options, &option_index);
 
    if (c == -1) break;
    last = c;

    switch (c) {
      case 0:
        if (! strcmp(long_options[option_index].name, "version")) {
          printf("pstatus - POPular POP server suite %s\n", VERSION);
          exit(RCODE_OK);
        } else if (! strcmp(long_options[option_index].name, "help")) {
          print_usage();
          exit(RCODE_OK);
        } else {
          fprintf(stderr, "%s: unknown option: %s\n", prgname, long_options[option_index].name);
          exit(RCODE_INTERNAL);
        }
        break;
      case 'p':		/* program to connect to */
	if (statefile[0]) {
	  fprintf(stderr, "%s: you can't use --file and --program at the same time (or twice)\n", prgname);
	  exit(RCODE_CMDLINE);
	}
        if (!strcmp(optarg, "pserv") || !strcmp(optarg, "serv")) {
          strlcpy(statefile, RUN_DIR "/" PSERV_STATE_FILE, sizeof(statefile));
	} else if (!strcmp(optarg, "pproxy") || !strcmp(optarg, "proxy")) {
          strlcpy(statefile, RUN_DIR "/" PPROXY_STATE_FILE, sizeof(statefile));
	} else {
	  fprintf(stderr, "%s: unknown program: %s\n", prgname, optarg);
	  exit(RCODE_CMDLINE);
	}
	break;
      case 'f':		/* filename with state information */
	if (statefile[0]) {
	  fprintf(stderr, "%s: you can't use --file and --program at the same time (or twice)\n", prgname);
	  exit(RCODE_CMDLINE);
	}
	strlcpy(statefile, optarg, sizeof(statefile));
        break;
      case 'l':		/* loop */
        loop = get_int(optarg, 0, 3600, -1, -2, 1);
	switch (loop) {
	  case -1:
	    fprintf(stderr, "%s: loop argument must be between 0 and 3600\n", prgname);
	    exit(RCODE_CMDLINE);
	  case -2:
	    fprintf(stderr, "%s: loop parameter must be a number\n", prgname);
	    exit(RCODE_CMDLINE);
	  case 0:
	    loop = 1;
	    break;
	}
        break;
      case 'a':		/* active */
	active = 1;
        break;
      case 'r':		/* range */
	{
	  char *p = strchr(optarg, '-');
	  if (!p) {			/* range: n */
	    slot_min = get_int(optarg, 0, 10000, -1, -1, -1);
	    slot_max = slot_min;
	  } else if (p == optarg) {	/* range: -n */
	    slot_max = get_int(optarg+1, 0, 10000, -1, -1, -1);
	  } else if (p[1] == '\0') {	/* range: n- */
	    *p = '\0';
	    slot_min = get_int(optarg, 0, 10000, -1, -1, -1);
	  } else {			/* range: n-m */
	    *p = '\0';
	    slot_min = get_int(optarg, 0, 10000, -1, -1, -1);
	    slot_max = get_int(p+1, 0, 10000, -1, -1, -1);
	  }
	}
	if (slot_min < 0 || slot_max < 0) {
	  fprintf(stderr, "%s: illegal argument to range parameter\n", prgname);
	  exit(RCODE_CMDLINE);
	}
	if (slot_max < slot_min) {
	  int c = slot_min;
	  slot_min = slot_max;
	  slot_max = c;
	}
        break;
      default:
        fprintf(stderr, "Try `%s --help' for more information.\n", prgname);
        exit(RCODE_CMDLINE);
    }
  }

  if (loop == -1) loop=0;

  if (optind != argc) {
    fprintf(stderr, "%s: invalid extra arguments.\nTry `%s --help' for more information.\n", prgname, prgname);
    exit(RCODE_CMDLINE);
  }

  if (! statefile[0]) {
    if (access(RUN_DIR "/" PPROXY_STATE_FILE, R_OK) == 0) {
      strlcpy(statefile, RUN_DIR "/" PPROXY_STATE_FILE, sizeof(statefile));
    } else if (access(RUN_DIR "/" PSERV_STATE_FILE, R_OK) == 0) {
      strlcpy(statefile, RUN_DIR "/" PSERV_STATE_FILE, sizeof(statefile));
    } else {
      fprintf(stderr, "%s: no state file found. Try `%s --help' for more information.\n", prgname, prgname);
      exit(RCODE_CMDLINE);
    }
  }


  /***************************************************************************
    Open state file and mmap it.
  ***************************************************************************/
  statusfd = open(statefile, O_RDONLY);
  if (statusfd == -1) {
    fprintf(stderr, "%s: open of state file failed: %s\n", prgname, strerror(errno));
    exit(RCODE_ERR);
  }
  if (fstat(statusfd, &statbuf) == -1) {
    fprintf(stderr, "%s: stat of state file failed: %s\n", prgname, strerror(errno));
    exit(RCODE_ERR);
  }
  mp = mmap(NULL, statbuf.st_size, PROT_READ, MAP_SHARED, statusfd, 0);
  if (mp == (caddr_t)-1) {
    fprintf(stderr, "%s: mmap of state file failed: %s\n", prgname, strerror(errno));
    exit(RCODE_ERR);
  }


  /***************************************************************************
    Get some memory and copy content of state file over with locking.
  ***************************************************************************/
  sd = (struct shmem_desc *) malloc(statbuf.st_size);
  if (sd == NULL) {
    fprintf(stderr, "%s: out of memory\n", prgname);
    exit(RCODE_ERR);
  }
  copy_shmem(statusfd, sd, mp, statbuf.st_size);


  /***************************************************************************
    Check for magic.
  ***************************************************************************/
  if (strcmp(sd->magic, "POPULAR")) {
    fprintf(stderr, "%s: this is not a state file: %s\n", prgname, statefile);
    exit(RCODE_ERR);
  }


  /***************************************************************************
    Check state file type and call appropriate function for printing.
  ***************************************************************************/
  if (loop) puts(CLEARSCREEN);
  if (!strcmp(sd->type, "PROXY")) {
    while (1) {
      if (loop) puts(CURSORHOME);
      copy_shmem(statusfd, sd, mp, statbuf.st_size);
      print_proxy((struct shmem_proxy *) sd);
      if (! loop) break;
      sleep(loop);
    }
  } else if (!strcmp(sd->type, "SERVER")) {
    while (1) {
      if (loop) puts(CURSORHOME);
      copy_shmem(statusfd, sd, mp, statbuf.st_size);
      print_backend((struct shmem_backend *) sd);
      if (! loop) break;
      sleep(loop);
    }
  } else {
    fprintf(stderr, "%s: unknown file type: `%s'\n", prgname, sd->type);
    exit(RCODE_ERR);
  }
  exit(RCODE_OK);
}


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


syntax highlighted by Code2HTML, v. 0.9.1