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