/***************************************************************************** POPular -- A POP3 server and proxy for large mail systems $Id: daemon.c,v 1.10 2003/11/24 19:23:30 sqrt Exp $ http://www.remote.org/jochen/mail/popular/ ****************************************************************************** Copyright (C) 1999-2003 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" /***************************************************************************** daemonize() *****************************************************************************/ void daemonize(const char *prg) { switch (fork()) { case 0: /* child */ break; case -1: /* error */ fprintf(stderr, "%s: fork failed: %s\n", prg, strerror(errno)); exit(RCODE_ERR); default: /* parent */ exit(RCODE_OK); } (void) close(0); (void) close(1); (void) close(2); (void) chdir("/"); (void) setsid(); } /***************************************************************************** set_keepalive() Set TCP keepalive on a socket. *****************************************************************************/ int set_keepalive(int socket) { int one=1; return setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(int)); } /***************************************************************************** prepare_statefile() *****************************************************************************/ void * prepare_statefile(const char *dir, const char *templ, const char *link, int size, int *ofd) { char fn[strlen(dir) + strlen(templ) + 10]; char ln[strlen(dir) + strlen(link) + 10]; caddr_t mp; int fd; char sbuf[1] = ""; char *p; strlcpy(fn, dir, sizeof(fn)); strlcat(fn, "/", sizeof(fn)); snprintf(fn+strlen(fn), sizeof(fn)-strlen(fn), templ, getpid()); fd = open(fn, O_RDWR|O_CREAT, 0644); if (fd == -1) { /* XLOG-DOC:SOS:0135:open_statefile * Open of the state file failed. The program will be terminated. */ xlog_printf(xlog_sos, 0x0135, "open_statefile filename='%s' errno=%d errmsg='%s'", fn, errno, strerror(errno)); exit(RCODE_ERR); } if (lseek(fd, size, SEEK_SET) == -1) { /* XLOG-DOC:SOS:0136:seek_statefile * Seeking inside the state file failed. The program will be * terminated. Try deleting the state file before restarting. */ xlog_printf(xlog_sos, 0x0136, "seek_statefile errno=%d errmsg='%s'", errno, strerror(errno)); exit(RCODE_ERR); } if (rel_write(fd, sbuf, 1) != 1) { /* XLOG-DOC:SOS:0137:write_statefile * Writing to state file failed. The program will be terminated. * Try deleting the state file before restarting. */ xlog_printf(xlog_sos, 0x0137, "write_statefile errno=%d errmsg='%s'", errno, strerror(errno)); exit(RCODE_ERR); } mp = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (mp == (caddr_t) -1) { /* XLOG-DOC:SOS:0138:mmap_statefile * Mmapping the state file failed. The program will be terminated. * Try deleting the state file before restarting. */ xlog_printf(xlog_sos, 0x0138, "mmap_statefile errno=%d errmsg='%s'", errno, strerror(errno)); exit(RCODE_ERR); } memset(mp, 0, size); strlcpy(ln, dir, sizeof(ln)); strlcat(ln, "/", sizeof(ln)); strlcat(ln, link, sizeof(ln)); p = strrchr(fn, '/'); (void)unlink(ln); if (symlink(++p, ln) < 0) { /* XLOG-DOC:ADM:0139:symlink_statefile_failed * The symlinking of the real state file to its symbolical name * failed. The server will still work, but the pstatus program * will not find the state file. */ xlog_printf(xlog_adm, 0x0139, "symlink_statefile_failed linkname='%s' name='%s' errno=%d errmsg='%s'", p, ln, errno, strerror(errno)); } if (ofd) *ofd = fd; return (void *)mp; } /***************************************************************************** unlink_in_rundir() *****************************************************************************/ void unlink_in_rundir(const char *dir, const char *templ, const char *link) { char fn[strlen(dir) + max(strlen(templ), strlen(link)) + 10]; strlcpy(fn, dir, sizeof(fn)); strlcat(fn, "/", sizeof(fn)); snprintf(fn+strlen(fn), sizeof(fn)-strlen(fn), templ, getpid()); if (unlink(fn) < 0) { /* XLOG-DOC:ADM:0141:unlink_file_in_rundir_failed * An unlink() operation in the run directory failed. The file can * be a pid file, the state file or the control socket. */ xlog_printf(xlog_adm, 0x0141, "unlink_file_in_rundir_failed filename='%s' errno=%d errmsg='%s'", fn, errno, strerror(errno)); return; } strlcpy(fn, dir, sizeof(fn)); strlcat(fn, "/", sizeof(fn)); strlcat(fn, link, sizeof(fn)); if (access(fn, F_OK) != 0) { if (unlink(fn) < 0) { /* XLOG-DOC:ADM:0142:unlink_link_in_rundir_failed * An unlink() operation in the run directory failed. The filename is * a link to either a pid file, a state file or a control socket. */ xlog_printf(xlog_adm, 0x0142, "unlink_link_in_rundir_failed filename='%s' errno=%d errmsg='%s'", fn, errno, strerror(errno)); } } return; } /***************************************************************************** write_pidfile() *****************************************************************************/ void write_pidfile(const char *dir, const char *templ, const char *link) { char fn[strlen(dir) + strlen(templ) + 10]; char ln[strlen(dir) + strlen(link) + 10]; int fd; char buf[10]; char *p; strlcpy(fn, dir, sizeof(fn)); strlcat(fn, "/", sizeof(fn)); snprintf(fn+strlen(fn), sizeof(fn)-strlen(fn), templ, getpid()); fd = open(fn, O_WRONLY|O_CREAT, 0644); if (fd < 0) { /* XLOG-DOC:ADM:0140:pidfile_open_failed * The opening of the pidfile failed. The server will continue to run. */ xlog_printf(xlog_adm, 0x0140, "pidfile_open_failed file='%s' errno=%d errmsg='%s'", fn, errno, strerror(errno)); return; } snprintf(buf, sizeof(buf), "%d\n", getpid()); rel_write(fd, buf, strlen(buf)); close(fd); strlcpy(ln, dir, sizeof(ln)); strlcat(ln, "/", sizeof(ln)); strlcat(ln, link, sizeof(ln)); p = strrchr(fn, '/'); (void)unlink(ln); if (symlink(++p, ln) < 0) { /* XLOG-DOC:ADM:008d:pidfile_symlink_failed * The symlink to the pidfile failed. The server will continue to run. */ xlog_printf(xlog_adm, 0x008d, "pidfile_symlink_failed file='%s' link='%s' errno=%d errmsg='%s'", p, ln, errno, strerror(errno)); } } /***************************************************************************** open_ctrl_socket() *****************************************************************************/ int open_ctrl_socket(const char *dir, const char *templ, const char *link, mode_t mask) { struct sockaddr_un sockun; char ln[strlen(dir) + strlen(link) + 10]; mode_t oldmask; int fd; char *p; fd = socket(AF_UNIX, SOCK_DGRAM, 0); if (fd < 0) { /* XLOG-DOC:SOS:013a:control_socket_open_failed * Opening of the UNIX domain control socket failed. The program will be * terminated. */ xlog_printf(xlog_sos, 0x013a, "control_socket_open_failed errno=%d errmsg='%s'", errno, strerror(errno)); exit(RCODE_ERR); } memset(&sockun, 0, sizeof(sockun)); sockun.sun_family = AF_UNIX; strlcpy(sockun.sun_path, dir, sizeof(sockun.sun_path)); strlcat(sockun.sun_path, "/", sizeof(sockun.sun_path)); snprintf(sockun.sun_path+strlen(sockun.sun_path), sizeof(sockun.sun_path)-strlen(sockun.sun_path), templ, getpid()); oldmask = umask(mask); if (bind(fd, (struct sockaddr *) &sockun, sizeof(sockun)) < 0) { /* XLOG-DOC:SOS:013b:control_bind_failed * Binding the UNIX domain control socket failed. The program will be * terminated. */ xlog_printf(xlog_sos, 0x013b, "control_bind_failed socket='%s' errno=%d errmsg='%s'", sockun.sun_path, errno, strerror(errno)); exit(RCODE_ERR); } (void) umask(oldmask); strlcpy(ln, dir, sizeof(ln)); strlcat(ln, "/", sizeof(ln)); strlcat(ln, link, sizeof(ln)); p = strrchr(sockun.sun_path, '/'); (void)unlink(ln); if (symlink(++p, ln) != 0) { /* XLOG-DOC:SOS:013c:control_symlink_failed * Linking the UNIX domain control socket to the canonical name failed. * The programm will be terminated. */ xlog_printf(xlog_sos, 0x013c, "control_symlink_failed socket='%s' linkname='%s' errno=%d errmsg='%s'", sockun.sun_path, link, errno, strerror(errno)); exit(RCODE_ERR); } return fd; } /***************************************************************************** load_ok() Checks wether the current load is smaller than the given . If is 0, the check always returns true. *****************************************************************************/ int load_ok(int maxload) { static char *statenames[] = { "OK", "WARN", "MAX" }; static int state=0; int oldstate = state; long load; if (maxload == 0) return 1; load = get_load(); if (load < 0) return 1; switch (state) { case 0: if (load >= maxload * 9 / 10) state=1; if (load >= maxload) state=2; break; case 1: if (load < maxload * 8 / 10) state=0; if (load >= maxload) state=2; break; case 2: if (load < maxload * 9 / 10) state=1; if (load < maxload * 8 / 10) state=0; break; default: /* XLOG-DOC:BUG:0171:illegal_load_state * The state in sess_load() has an illegal value. This can never happen. * The server keeps going but might log strange things. */ xlog_printf(xlog_bug, 0x0171, "illegal_load_state"); return state != 2; } /* XLOG-DOC:ADM:0172:load_state * The load state is printed. This can be one of 'OK', 'WARN', 'MAX'. * See the documentation for the exact meaning of these messages. */ if (oldstate != state) xlog_printf(xlog_adm, 0x0172, "load_state state=%s load=%ld", statenames[state], load); return state != 2; } /***************************************************************************** sess_ok() *****************************************************************************/ int sess_ok(int used, int max, const char *dir, const char *file) { static char *statenames[] = { "OK", "WARN", "MAX" }; static int state=0; static int flagfile=0; int oldstate = state; if (flagfile == 1 && used < max-1) { char buf[MAXBUF]; flagfile = 0; strlcpy(buf, dir, sizeof(buf)); strlcat(buf, "/", sizeof(buf)); strlcat(buf, file, sizeof(buf)); if (unlink(buf) != 0) { /* XLOG-DOC:SOS:0174:unlink_failed * Unlink of a flag file failed. */ xlog_printf(xlog_sos, 0x0174, "unlink_failed file='%s/%s' errno=%d errmsg='%s'", dir, file, errno, strerror(errno)); } } if (flagfile == 0 && used >= max && dir && file) { flagfile = 1; if (touch(dir, file) != 0) { /* XLOG-DOC:SOS:0173:touch_failed * Creating or 'touching' of a flag file failed. */ xlog_printf(xlog_sos, 0x0173, "touch_failed file='%s/%s' errno=%d errmsg='%s'", dir, file, errno, strerror(errno)); } } switch (state) { case 0: if (used >= max * 9 / 10) state=1; if (used >= max) state=2; break; case 1: if (used < max * 8 / 10) state=0; if (used >= max) state=2; break; case 2: if (used < max * 9 / 10) state=1; break; default: /* XLOG-DOC:BUG:016f:illegal_session_state * The state in sess_ok() has an illegal value. This can never happen. * The server keeps going but might log strange things. */ xlog_printf(xlog_bug, 0x016f, "illegal_session_state"); return used < max; } /* XLOG-DOC:ADM:0170:session_state * The session state is printed. This can be one of 'OK', 'WARN', 'MAX'. * See the documentation for the exact meaning of these messages. */ if (oldstate != state) xlog_printf(xlog_adm, 0x0170, "session_state state=%s", statenames[state]); return used < max; } /***************************************************************************** reapchildren() Reap all deceased children, logging the event only if they died an unnatural death. The function freesess will be called with the pid of any dead child. *****************************************************************************/ void reapchildren(void (*freesess)(pid_t)) { pid_t pid; int status; while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { if (WIFEXITED(status) && WEXITSTATUS(status)) { /* XLOG-DOC:ADM:0020:child_died * A child of pserv or pproxy died with non-zero exit code. See the * log entries of the child for more details about what happend. */ xlog_printf(xlog_adm, 0x0020, "child_died pid=%d status=%d", pid, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { if (WTERMSIG(status) == SIGALRM) { /* XLOG-DOC:ERR:017c:child_died_timeout * A child of pserv or pproxy died because of an ALRM signal. This * means that 'sessiontimeout' (or, for pproxy, 'authtimeout') ran * out. */ xlog_printf(xlog_err, 0x017c, "child_died_timeout pid=%d", pid); } else { /* XLOG-DOC:SOS:0021:child_died_signal * A child of pserv or pproxy 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, 0x0021, "child_died_signal pid=%d signal=%d", pid, WTERMSIG(status)); } } (*freesess)(pid); } } /** THE END *****************************************************************/