/*****************************************************************************
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 <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"
/*****************************************************************************
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 <maxload>. If
<maxload> 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 *****************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1