/*****************************************************************************
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