/***************************************************************************** POPular -- A POP3 server and proxy for large mail systems $Id: pcheckd.c,v 1.35 2003/11/22 21:27:22 sqrt Exp $ http://www.remote.org/jochen/mail/popular/ ****************************************************************************** Copyright (C) 1999-2002 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" #include "daemon.h" static char mbdir[MAXBUF]; static char maxsession_file[MAXBUF]; static int load_is_ok; static long num_requests; static long num_mailchecks; static long num_mailchecks_old; static long num_empty; static long num_not_empty; static long num_load_too_high; static long num_maxsession_reached; int debug; int child_pid[MAX_PCHECKD_CHILDREN]; /***************************************************************************** handle_mailcheck() *****************************************************************************/ int handle_mailcheck(const char *mailbox, char *out) { DIR *d; int n, len, i; char dirname[MAXLEN_DIRNAME]; if (mailbox[0] == '/') { /* XLOG-DOC:ADM:0072:invalid_mailbox * The mailbox name that the client sent is invalid. The name is not * allowed to start with a '/' */ xlog_printf(xlog_adm, 0x0072, "invalid_mailbox mailbox='%s'", mailbox); return strlcpy(out, "-ERR no mailbox", MAXLEN_MAILCHECK); } for (i=0; mailbox[i]; i++) { if ( !(isalnum((int)(mailbox[i])) || mailbox[i] == '.' || mailbox[i] == '_' || mailbox[i] == '-' || mailbox[i] == '+' || mailbox[i] == '/' || mailbox[i] == '%' || mailbox[i] == '=' ) || (mailbox[i] == '.' && mailbox[i+1] == '.') ) { /* XLOG-DOC:ADM:0154:bad_mailbox * The mailbox name that the proxy sent to the pserv has funny * chars in it. Only the following chars are allowed: a-z, A-Z, 0-9, * '.', '_', '-', '+', '/', '=', '%'. Two adjacent dots ("..") are not * allowed. */ xlog_printf(xlog_adm, 0x0154, "bad_mailbox mailbox='%s'", mailbox); return strlcpy(out, "-ERR no mailbox", MAXLEN_MAILCHECK); } } (void) strlcpy(dirname, mbdir, sizeof(dirname)); (void) strlcat(dirname, "/", sizeof(dirname)); (void) strlcat(dirname, mailbox, sizeof(dirname)); len = strlcat(dirname, "/new", sizeof(dirname)); d = opendir(dirname); if (! d) { /* XLOG-DOC:ERR:0073:opendir_failed * The 'new' directory for the given mailbox can't be opened. This * probably means that the mailbox hasn't been created yet. */ xlog_printf(xlog_err, 0x0073, "opendir_failed dir='%s' errno=%d errmsg='%s'", dirname, errno, strerror(errno)); return strlcpy(out, "-ERR no mailbox", MAXLEN_MAILCHECK); } n=0; while (readdir(d)) { n++; if (n > 2) { closedir(d); num_not_empty++; return strlcpy(out, "+OK 2 new mail", MAXLEN_MAILCHECK); } } closedir(d); (void) strlcpy(dirname+len-3, "cur", 4); d = opendir(dirname); if (! d) { /* XLOG-DOC:ADM:0074:opendir_failed * The 'cur' directory for the given mailbox can't be opened. */ xlog_printf(xlog_adm, 0x0074, "opendir_failed dir='%s' errno=%d errmsg='%s'", dirname, errno, strerror(errno)); return strlcpy(out, "-ERR no mailbox", MAXLEN_MAILCHECK); } n=0; while (readdir(d)) { n++; if (n > 2) { closedir(d); num_not_empty++; return strlcpy(out, "+OK 1 mail", MAXLEN_MAILCHECK); } } closedir(d); num_empty++; return strlcpy(out, "+OK 0 no mail", MAXLEN_MAILCHECK); } /***************************************************************************** handle_loadcheck() *****************************************************************************/ int handle_loadcheck(const char *in, char *out) { long load = get_load(); if (load < 0) { (void) strlcpy(out, "-ERR not available", MAXLEN_MAILCHECK); } else { snprintf(out, MAXLEN_MAILCHECK, "+OK %ld", load); } return strlen(out); } /***************************************************************************** handle_request() *****************************************************************************/ int handle_request(const char *in, char *out) { num_requests++; if (in[0] == '*') { if (in[1] == 'L') { return handle_loadcheck(in+2, out); } else if (in[1] == 'M') { if (load_is_ok) { struct stat sbuf; int sr = stat(maxsession_file, &sbuf); if (sr == -1) { num_mailchecks++; return handle_mailcheck(in+2, out); } else { num_maxsession_reached++; return strlcpy(out, "+OK 4 maxsession reached", MAXLEN_MAILCHECK); } } else { num_load_too_high++; return strlcpy(out, "+OK 3 load too high", MAXLEN_MAILCHECK); } } else { /* XLOG-DOC:ADM:0075:unknown_request * The client sent an unknown request. Allowed are 'L' for load check * and 'M' for mailbox check. */ xlog_printf(xlog_adm, 0x0075, "unknown_request request='%s'", in); (void) strlcpy(out, "-ERR unknown request", MAXLEN_MAILCHECK); return strlen(out); } } else { /* compatibility for old versions */ num_mailchecks_old++; return handle_mailcheck(in, out); } } /***************************************************************************** pcheckd_reapchildren() Reap all deceased children, logging the event only if they died an unnatural death. *****************************************************************************/ void pcheckd_reapchildren() { pid_t pid; int status, i; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status) && WEXITSTATUS(status)) { /* XLOG-DOC:ADM:0156:child_died * A child of pcheckd died with non-zero exit code. See the log entries * of the child for more details about what happened. */ xlog_printf(xlog_adm, 0x0156, "child_died pid=%d status=%d", pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { /* XLOG-DOC:ADM:0157:child_died_signal * A child of pcheckd died because of a signal. This can either mean * that there is a bug in the program of the system has big problems * (like running out of memory). */ xlog_printf(xlog_sos, 0x0157, "child_died_signal pid=%d signal=%d", pid, WTERMSIG(status)); } DEBUG1(DG_MAIN, "reapchildren", "child died pid=%d", pid); for (i=0; ih_addr_list)[0] == NULL) { fprintf(stderr, "%s: unkown host or IP: %s\n", prgname, host); exit(RCODE_ERR); } memcpy(&(sa.sin_addr.s_addr), hostent->h_addr_list[0], sizeof(sa.sin_addr.s_addr)); } else { sa.sin_addr.s_addr = htonl(INADDR_ANY); } if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { fprintf(stderr, "%s: bind failed: %s\n", prgname, strerror(errno)); exit(RCODE_ERR); } /*************************************************************************** Daemonize unless --nodaemon option was given. ***************************************************************************/ if (daemon) daemonize(prgname); /*************************************************************************** Open log file. ***************************************************************************/ if (xlog_open(PCHECKD_PRG_NAME, logfile, PCHECKD_LOG_MODE) < 0) { fprintf(stderr, "%s: couldn't open logfile '%s': %s\n", prgname, logfile, strerror(errno)); exit(RCODE_ERR); } /* XLOG-DOC:INF:0070:start * Pcheckd has been started and partially initialized. */ xlog_printf(xlog_inf, 0x0070, "start version='%s' port=%d mailboxdir='%s' emptyload=%d", VERSION, checkport, mbdir, emptyload); /*************************************************************************** Open pid file. ***************************************************************************/ write_pidfile(rundir, PCHECKD_PID_TEMPL, PCHECKD_PID_FILE); /*************************************************************************** Setup signal handling. ***************************************************************************/ signal_init(1); if (num_fork) { int i; for (i=0; i 0) kill(child_pid[i], SIGTERM); } } exit(RCODE_OK); case SIGHUP: /* XLOG-DOC:INF:0144:stat * Statistics from pcheckd: 'requests' is the number of all * requests that pcheckd got, 'mailchecks' is the number of * new format mailcheck requests, 'mailchecks_old' the number * of old format mailcheck requests, 'empty' the number of * requests finding an empty mailbox, 'not_empty' the number of * requests finding a not empty mailbox, 'load_too_high' * the number of requests answered with that message, and * 'maxsession_reached' the number of messages send while the * maximum number of pserv sessions was used. The * statistics are logged and cleared on SIGHUP. */ xlog_printf(xlog_inf, 0x0144, "stat requests=%ld mailchecks=%ld mailchecks_old=%ld empty=%ld not_empty=%ld load_too_high=%ld maxsession_reached=%ld", num_requests, num_mailchecks, num_mailchecks_old, num_empty, num_not_empty, num_load_too_high, num_maxsession_reached); num_requests = 0; num_mailchecks = 0; num_mailchecks_old = 0; num_empty = 0; num_not_empty = 0; num_load_too_high = 0; num_maxsession_reached = 0; xlog_reopen(logfile, PCHECKD_LOG_MODE); break; case SIGUSR1: /* XLOG-DOC:INF:0079:debug_disabled * pcheckd received a SIGUSR1 and disabled debuglogging. */ xlog_printf(xlog_inf, 0x0079, "debug_disabled"); debug=0; break; case SIGUSR2: /* XLOG-DOC:INF:007a:debug_enabled * pcheckd received a SIGUSR2 and enabled debuglogging. */ xlog_printf(xlog_inf, 0x007a, "debug_enabled"); debug=1; break; default: /* ignore other signals */ break; } } if (r == 0) continue; n = recvfrom(s, inmsg, MAXLEN_MAILCHECK, 0, &remote, &remotelen); if (n >= 0) { inmsg[n] = '\0'; if (n>0 && inmsg[n-1] == '\n') inmsg[--n] = '\0'; if (n>0 && inmsg[n-1] == '\r') inmsg[--n] = '\0'; DEBUG1(DG_MAIN, "main", "recv_msg msg='%s'", inmsg); if ((n = handle_request(inmsg, outmsg)) > 0) { DEBUG1(DG_MAIN, "main", "send_msg msg='%s'", outmsg); (void) strlcat(outmsg, "\n", sizeof(outmsg)); if (sendto(s, outmsg, n+1, 0, &remote, remotelen) != n+1) { /* XLOG-DOC:ERR:0076:sendto_failed * Sending the answer of the requested check back to the client * failed. */ xlog_printf(xlog_err, 0x0076, "sendto_failed errno=%d errmsg='%s'", errno, strerror(errno)); } } } } } /** THE END *****************************************************************/