/* $Id: milter-regex.c,v 1.3 2007/08/03 22:11:48 dhartmei Exp $ */
/*
* Copyright (c) 2003-2006 Daniel Hartmeier
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
static const char rcsid[] = "$Id: milter-regex.c,v 1.3 2007/08/03 22:11:48 dhartmei Exp $";
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#ifdef __linux__
#include <grp.h>
#endif
#include <libmilter/mfapi.h>
#include "eval.h"
extern void die(const char *);
extern int parse_ruleset(const char *, struct ruleset **, char *,
size_t);
static const char *rule_file_name = "/usr/local/etc/milter-regex.conf";
static int debug = 0;
static int quiet = 0;
static pthread_mutex_t mutex;
struct context {
struct ruleset *rs;
int *res;
char buf[2048]; /* longer body lines are wrapped */
unsigned pos; /* write position within buf */
char host_name[128];
char host_addr[64];
char helo[128];
char hdr_from[128];
char hdr_to[128];
char hdr_subject[128];
char *quarantine;
};
static sfsistat setreply(SMFICTX *, struct context *,
const struct action *);
static struct ruleset *get_ruleset(void);
static sfsistat cb_connect(SMFICTX *, char *, _SOCK_ADDR *);
static sfsistat cb_helo(SMFICTX *, char *);
static sfsistat cb_envfrom(SMFICTX *, char **);
static sfsistat cb_envrcpt(SMFICTX *, char **);
static sfsistat cb_header(SMFICTX *, char *, char *);
static sfsistat cb_eoh(SMFICTX *);
static sfsistat cb_body(SMFICTX *, u_char *, size_t);
static sfsistat cb_eom(SMFICTX *);
static sfsistat cb_close(SMFICTX *);
static void usage(const char *);
static void msg(int, struct context *, const char *, ...);
#define USER "mailnull"
#define OCONN "unix:/var/run/milter-regex/sock"
#define OPID "/var/run/milter-regex/milter-regex.pid"
#define RCODE_REJECT "554"
#define RCODE_TEMPFAIL "451"
#define XCODE_REJECT "5.7.1"
#define XCODE_TEMPFAIL "4.7.1"
#define MAXRS 16
/* Define what sendmail macros should be queried in what context (phase)
* with smfi_getsymval(). Whether sendmail actually provides specific
* values depends on configuration of confMILTER_MACROS_*
*/
struct {
const char *phase;
const char *name;
} macro[] = {
{ "connect", "{daemon_name}" },
{ "connect", "{if_name}" },
{ "connect", "{if_addr}" },
{ "connect", "j" },
{ "connect", "_" },
{ "helo", "{tls_version}" },
{ "helo", "{cipher}" },
{ "helo", "{cipher_bits}" },
{ "helo", "{cert_subject}" },
{ "helo", "{cert_issuer}" },
{ "envfrom", "i" },
{ "envfrom", "{auth_type}" },
{ "envfrom", "{auth_authen}" },
{ "envfrom", "{auth_ssf}" },
{ "envfrom", "{auth_author}" },
{ "envfrom", "{mail_mailer}" },
{ "envfrom", "{mail_host}" },
{ "envfrom", "{mail_addr}" },
{ "envrcpt", "{rcpt_mailer}" },
{ "envrcpt", "{rcpt_host}" },
{ "envrcpt", "{rcpt_addr}" },
{ NULL, NULL }
};
#if __linux__ || __sun__
#define ST_MTIME st_mtime
extern size_t strlcpy(char *, const char *, size_t);
#else
#define ST_MTIME st_mtimespec
#endif
static void
mutex_lock(void)
{
if (pthread_mutex_lock(&mutex))
die("pthread_mutex_lock");
}
static void
mutex_unlock(void)
{
if (pthread_mutex_unlock(&mutex))
die("pthread_mutex_unlock");
}
#ifdef __sun__
int
daemon(int nochdir, int noclose)
{
pid_t pid;
int fd;
if ((pid = fork()) < 0) {
fprintf(stderr, "fork: %s\n", strerror(errno));
return (1);
} else if (pid > 0)
_exit(0);
if ((pid = setsid()) == -1) {
fprintf(stderr, "setsid: %s\n", strerror(errno));
return (1);
}
if ((pid = fork()) < 0) {
fprintf(stderr, "fork: %s\n", strerror(errno));
return (1);
} else if (pid > 0)
_exit(0);
if (!nochdir && chdir("/")) {
fprintf(stderr, "chdir: %s\n", strerror(errno));
return (1);
}
if (!noclose) {
dup2(fd, fileno(stdout));
dup2(fd, fileno(stderr));
dup2(open("/dev/null", O_RDONLY, 0), fileno(stdin));
}
return (0);
}
#endif
static sfsistat
setreply(SMFICTX *ctx, struct context *context, const struct action *action)
{
int result = SMFIS_CONTINUE;
switch (action->type) {
case ACTION_REJECT:
msg(LOG_NOTICE, context, "REJECT: %s, Helo: %s, From: %s, "
"To: %s, Subject: %s", action->msg, context->helo,
context->hdr_from, context->hdr_to, context->hdr_subject);
result = SMFIS_REJECT;
break;
case ACTION_TEMPFAIL:
msg(LOG_NOTICE, context, "TEMPFAIL: %s, Helo: %s, From: %s, "
"To: %s, Subject: %s", action->msg, context->helo,
context->hdr_from, context->hdr_to, context->hdr_subject);
result = SMFIS_TEMPFAIL;
break;
case ACTION_QUARANTINE:
if (context->quarantine != NULL)
free(context->quarantine);
context->quarantine = strdup(action->msg);
break;
case ACTION_DISCARD:
msg(LOG_NOTICE, context, "DISCARD, Helo: %s, From: %s, "
"To: %s, Subject: %s", context->helo, context->hdr_from,
context->hdr_to, context->hdr_subject);
result = SMFIS_DISCARD;
break;
case ACTION_ACCEPT:
msg(LOG_INFO, context, "ACCEPT, Helo: %s, From: %s, "
"To: %s, Subject: %s", context->helo, context->hdr_from,
context->hdr_to, context->hdr_subject);
result = SMFIS_ACCEPT;
break;
}
if (action->type == ACTION_REJECT &&
smfi_setreply(ctx, RCODE_REJECT, XCODE_REJECT,
(char *)action->msg) != MI_SUCCESS)
msg(LOG_ERR, context, "smfi_setreply");
if (action->type == ACTION_TEMPFAIL &&
smfi_setreply(ctx, RCODE_TEMPFAIL, XCODE_TEMPFAIL,
(char *)action->msg) != MI_SUCCESS)
msg(LOG_ERR, context, "smfi_setreply");
return (result);
}
static struct ruleset *
get_ruleset(void)
{
static struct ruleset *rs[MAXRS] = {};
static int cur = 0;
static time_t last_check = 0;
static struct stat sbo;
time_t t = time(NULL);
int load = 0;
mutex_lock();
if (!last_check)
memset(&sbo, 0, sizeof(sbo));
if (t - last_check >= 10) {
struct stat sb;
last_check = t;
memset(&sb, 0, sizeof(sb));
if (stat(rule_file_name, &sb))
msg(LOG_ERR, NULL, "get_ruleset: stat: %s: %s",
rule_file_name, strerror(errno));
else if (memcmp(&sb.ST_MTIME, &sbo.ST_MTIME,
sizeof(sb.ST_MTIME))) {
memcpy(&sbo.ST_MTIME, &sb.ST_MTIME,
sizeof(sb.ST_MTIME));
load = 1;
}
}
if (load || rs[cur] == NULL) {
int i;
char err[8192];
msg(LOG_DEBUG, NULL, "loading new configuration file");
for (i = 0; i < MAXRS; ++i)
if (rs[i] != NULL && rs[i]->refcnt == 0) {
msg(LOG_DEBUG, NULL, "freeing unused ruleset "
"%d/%d", i, MAXRS);
free_ruleset(rs[i]);
rs[i] = NULL;
}
for (i = 0; i < MAXRS; ++i)
if (rs[i] == NULL)
break;
if (i == MAXRS)
msg(LOG_ERR, NULL, "all rulesets are in use, cannot "
"load new one", MAXRS);
else if (parse_ruleset(rule_file_name, &rs[i], err,
sizeof(err)) || rs[i] == NULL)
msg(LOG_ERR, NULL, "parse_ruleset: %s", err);
else {
msg(LOG_INFO, NULL, "configuration file %s loaded "
"successfully", rule_file_name);
cur = i;
}
}
mutex_unlock();
return (rs[cur]);
}
static struct action *
check_macros(SMFICTX *ctx, struct context *context, const char *phase)
{
struct action *action;
int i;
const char *v;
for (i = 0; macro[i].phase != NULL; ++i) {
if (strcmp(macro[i].phase, phase))
continue;
if ((v = smfi_getsymval(ctx, (char *)macro[i].name)) == NULL)
v = "";
msg(LOG_DEBUG, context, "macro %s = %s", macro[i].name, v);
if ((action = eval_cond(context->rs, context->res, COND_MACRO,
macro[i].name, v)) != NULL)
return (action);
}
return (NULL);
}
static sfsistat
cb_connect(SMFICTX *ctx, char *name, _SOCK_ADDR *sa)
{
struct context *context;
struct action *action;
context = calloc(1, sizeof(*context));
if (context == NULL) {
msg(LOG_ERR, NULL, "cb_connect: calloc: %s", strerror(errno));
return (SMFIS_ACCEPT);
}
context->rs = get_ruleset();
if (context->rs == NULL) {
free(context);
msg(LOG_ERR, NULL, "cb_connect: get_ruleset");
return (SMFIS_ACCEPT);
}
context->res = calloc(context->rs->maxidx, sizeof(*context->res));
if (context->res == NULL) {
free(context);
msg(LOG_ERR, NULL, "cb_connect: calloc: %s", strerror(errno));
return (SMFIS_ACCEPT);
}
if (smfi_setpriv(ctx, context) != MI_SUCCESS) {
free(context->res);
free(context);
msg(LOG_ERR, NULL, "cb_connect: smfi_setpriv");
return (SMFIS_ACCEPT);
}
context->rs->refcnt++;
strlcpy(context->host_name, name, sizeof(context->host_name));
strlcpy(context->host_addr, "unknown", sizeof(context->host_addr));
if (sa) {
switch (sa->sa_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
if (inet_ntop(AF_INET, &sin->sin_addr.s_addr,
context->host_addr, sizeof(context->host_addr)) ==
NULL)
msg(LOG_ERR, NULL, "cb_connect: inet_ntop: %s",
strerror(errno));
break;
}
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
if (inet_ntop(AF_INET6, &sin6->sin6_addr,
context->host_addr, sizeof(context->host_addr)) ==
NULL)
msg(LOG_ERR, NULL, "cb_connect: inet_ntop: %s",
strerror(errno));
break;
}
}
}
msg(LOG_DEBUG, context, "cb_connect('%s', '%s')",
context->host_name, context->host_addr);
if ((action = check_macros(ctx, context, "connect")) != NULL) {
/* can't really do this, delay */
/*return (setreply(ctx, context, action)); */
}
return (SMFIS_CONTINUE);
}
static sfsistat
cb_helo(SMFICTX *ctx, char *arg)
{
struct context *context;
const struct action *action;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_helo: smfi_getpriv");
return (SMFIS_ACCEPT);
}
strlcpy(context->helo, arg, sizeof(context->helo));
msg(LOG_DEBUG, context, "cb_helo('%s')", arg);
/* multiple HELO imply RSET in sendmail */
/* evaluate connect arguments here, because we can't call */
/* setreply from cb_connect */
eval_clear(context->rs, context->res, COND_CONNECT);
if ((action = eval_cond(context->rs, context->res, COND_CONNECT,
context->host_name, context->host_addr)) != NULL)
return (setreply(ctx, context, action));
if ((action = eval_end(context->rs, context->res, COND_CONNECT,
COND_MACRO)) !=
NULL)
return (setreply(ctx, context, action));
if ((action = check_macros(ctx, context, "helo")) != NULL)
return (setreply(ctx, context, action));
eval_clear(context->rs, context->res, COND_HELO);
if ((action = eval_cond(context->rs, context->res, COND_HELO,
arg, NULL)) != NULL)
return (setreply(ctx, context, action));
if ((action = eval_end(context->rs, context->res, COND_HELO,
COND_MACRO)) != NULL)
return (setreply(ctx, context, action));
return (SMFIS_CONTINUE);
}
static sfsistat
cb_envfrom(SMFICTX *ctx, char **args)
{
struct context *context;
const struct action *action;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_envfrom: smfi_getpriv");
return (SMFIS_ACCEPT);
}
/* multiple MAIL FROM indicate separate messages */
eval_clear(context->rs, context->res, COND_ENVFROM);
if (*args != NULL) {
msg(LOG_DEBUG, context, "cb_envfrom('%s')", *args);
if ((action = eval_cond(context->rs, context->res, COND_ENVFROM,
*args, NULL)) != NULL)
return (setreply(ctx, context, action));
}
if ((action = eval_end(context->rs, context->res, COND_ENVFROM,
COND_MACRO)) != NULL)
return (setreply(ctx, context, action));
if ((action = check_macros(ctx, context, "envfrom")) != NULL)
return (setreply(ctx, context, action));
return (SMFIS_CONTINUE);
}
static sfsistat
cb_envrcpt(SMFICTX *ctx, char **args)
{
struct context *context;
const struct action *action;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_envrcpt: smfi_getpriv");
return (SMFIS_ACCEPT);
}
/* multiple RCPT TO: possible */
eval_clear(context->rs, context->res, COND_ENVRCPT);
if (*args != NULL) {
msg(LOG_DEBUG, context, "cb_envrcpt('%s')", *args);
if ((action = eval_cond(context->rs, context->res, COND_ENVRCPT,
*args, NULL)) != NULL)
return (setreply(ctx, context, action));
}
if ((action = eval_end(context->rs, context->res, COND_ENVRCPT,
COND_MACRO)) != NULL)
return (setreply(ctx, context, action));
if ((action = check_macros(ctx, context, "envrcpt")) != NULL)
return (setreply(ctx, context, action));
return (SMFIS_CONTINUE);
}
static sfsistat
cb_header(SMFICTX *ctx, char *name, char *value)
{
struct context *context;
const struct action *action;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, context, "cb_header: smfi_getpriv");
return (SMFIS_ACCEPT);
}
msg(LOG_DEBUG, context, "cb_header('%s', '%s')", name, value);
if ((action = eval_end(context->rs, context->res, COND_MACRO,
COND_HEADER)) != NULL)
return (setreply(ctx, context, action));
if (!strcasecmp(name, "From"))
strlcpy(context->hdr_from, value, sizeof(context->hdr_from));
else if (!strcasecmp(name, "To"))
strlcpy(context->hdr_to, value, sizeof(context->hdr_to));
else if (!strcasecmp(name, "Subject"))
strlcpy(context->hdr_subject, value,
sizeof(context->hdr_subject));
if ((action = eval_cond(context->rs, context->res, COND_HEADER,
name, value)) != NULL)
return (setreply(ctx, context, action));
return (SMFIS_CONTINUE);
}
static sfsistat
cb_eoh(SMFICTX *ctx)
{
struct context *context;
const struct action *action;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_eoh: smfi_getpriv");
return (SMFIS_ACCEPT);
}
msg(LOG_DEBUG, context, "cb_eoh()");
memset(context->buf, 0, sizeof(context->buf));
context->pos = 0;
if ((action = eval_end(context->rs, context->res, COND_HEADER,
COND_BODY)) != NULL)
return (setreply(ctx, context, action));
return (SMFIS_CONTINUE);
}
static sfsistat
cb_body(SMFICTX *ctx, u_char *chunk, size_t size)
{
struct context *context;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_body: smfi_getpriv");
return (SMFIS_ACCEPT);
}
for (; size > 0; size--, chunk++) {
context->buf[context->pos] = *chunk;
if (context->buf[context->pos] == '\n' ||
context->pos == sizeof(context->buf) - 1) {
const struct action *action;
if (context->pos > 0 &&
context->buf[context->pos - 1] == '\r')
context->buf[context->pos - 1] = 0;
else
context->buf[context->pos] = 0;
context->pos = 0;
msg(LOG_DEBUG, context, "cb_body('%s')", context->buf);
if ((action = eval_cond(context->rs, context->res,
COND_BODY, context->buf, NULL)) != NULL)
return (setreply(ctx, context, action));
} else
context->pos++;
}
return (SMFIS_CONTINUE);
}
static sfsistat
cb_eom(SMFICTX *ctx)
{
struct context *context;
const struct action *action;
int result = SMFIS_ACCEPT;
if ((context = (struct context *)smfi_getpriv(ctx)) == NULL) {
msg(LOG_ERR, NULL, "cb_eom: smfi_getpriv");
return (SMFIS_ACCEPT);
}
msg(LOG_DEBUG, context, "cb_eom()");
if ((action = eval_end(context->rs, context->res, COND_BODY,
COND_MAX)) != NULL)
result = setreply(ctx, context, action);
else
msg(LOG_DEBUG, context, "ACCEPT, Helo: %s, From: %s, To: %s, "
"Subject: %s", context->helo, context->hdr_from,
context->hdr_to, context->hdr_subject);
if (context->quarantine != NULL) {
msg(LOG_NOTICE, context, "QUARANTINE: %s, Helo: %s, From: %s, "
"To: %s, Subject: %s", action->msg, context->helo,
context->hdr_from, context->hdr_to, context->hdr_subject);
if (smfi_quarantine(ctx, context->quarantine) != MI_SUCCESS)
msg(LOG_ERR, context, "cb_eom: smfi_quarantine");
}
return (result);
}
static sfsistat
cb_close(SMFICTX *ctx)
{
struct context *context;
context = (struct context *)smfi_getpriv(ctx);
msg(LOG_DEBUG, context, "cb_close()");
if (context != NULL) {
smfi_setpriv(ctx, NULL);
free(context->res);
if (context->quarantine != NULL)
free(context->quarantine);
context->rs->refcnt--;
free(context);
}
return (SMFIS_CONTINUE);
}
struct smfiDesc smfilter = {
"milter-regex", /* filter name */
SMFI_VERSION, /* version code -- do not change */
SMFIF_QUARANTINE, /* flags */
cb_connect, /* connection info filter */
cb_helo, /* SMTP HELO command filter */
cb_envfrom, /* envelope sender filter */
cb_envrcpt, /* envelope recipient filter */
cb_header, /* header filter */
cb_eoh, /* end of header */
cb_body, /* body block */
cb_eom, /* end of message */
NULL, /* message aborted */
cb_close /* connection cleanup */
};
static void
msg(int priority, struct context *context, const char *fmt, ...)
{
va_list ap;
char msg[8192];
if (LOG_PRI(priority) > LOG_INFO && quiet)
return;
va_start(ap, fmt);
if (context != NULL)
snprintf(msg, sizeof(msg), "%s: ", context->host_addr);
else
msg[0] = 0;
vsnprintf(msg + strlen(msg), sizeof(msg) - strlen(msg), fmt, ap);
if (debug)
printf("syslog: %s\n", msg);
else
syslog(priority, "%s", msg);
va_end(ap);
}
static void
usage(const char *argv0)
{
fprintf(stderr, "usage: %s [-d] [-c config] [-u user] "
"[-p pipe]\n", argv0);
exit(1);
}
void
die(const char *reason)
{
msg(LOG_ERR, NULL, "die: %s", reason);
smfi_stop();
sleep(60);
/* not reached, smfi_stop() kills thread */
abort();
}
int
main(int argc, char **argv)
{
int ch;
const char *oconn = OCONN;
const char *pid_file_name = OPID;
const char *user = USER;
sfsistat r = MI_FAILURE;
const char *ofile = NULL;
pid_t pid;
FILE *pid_fd = NULL;
tzset();
openlog("milter-regex", LOG_PID | LOG_NDELAY, LOG_DAEMON);
while ((ch = getopt(argc, argv, "c:dp:qr:u:")) != -1) {
switch (ch) {
case 'c':
rule_file_name = optarg;
break;
case 'd':
debug = 1;
break;
case 'p':
oconn = optarg;
break;
case 'q':
quiet = 1;
break;
case 'r':
pid_file_name = optarg;
break;
case 'u':
user = optarg;
break;
default:
usage(argv[0]);
}
}
if (argc != optind) {
fprintf(stderr, "unknown command line argument: %s ...",
argv[optind]);
usage(argv[0]);
}
if (!strncmp(oconn, "unix:", 5))
ofile = oconn + 5;
else if (!strncmp(oconn, "local:", 6))
ofile = oconn + 6;
if (ofile != NULL)
unlink(ofile);
/* drop privileges */
if (!getuid()) {
struct passwd *pw;
if ((pw = getpwnam(user)) == NULL) {
fprintf(stderr, "getpwnam: %s: %s\n", user,
strerror(errno));
return (1);
}
setgroups(1, &pw->pw_gid);
if (setegid(pw->pw_gid) || setgid(pw->pw_gid)) {
fprintf(stderr, "setgid: %s\n", strerror(errno));
return (1);
}
if (
#if ! ( __linux__ || __sun__ )
seteuid(pw->pw_uid) ||
#endif
setuid(pw->pw_uid)) {
fprintf(stderr, "setuid: %s\n", strerror(errno));
return (1);
}
}
if (pthread_mutex_init(&mutex, 0)) {
fprintf(stderr, "pthread_mutex_init\n");
goto done;
}
if (smfi_setconn((char *)oconn) != MI_SUCCESS) {
fprintf(stderr, "smfi_setconn: %s: failed\n", oconn);
goto done;
}
if (smfi_register(smfilter) != MI_SUCCESS) {
fprintf(stderr, "smfi_register: failed\n");
goto done;
}
if (eval_init(ACTION_ACCEPT)) {
fprintf(stderr, "eval_init: failed\n");
goto done;
}
/* daemonize (detach from controlling terminal) */
if (!debug && daemon(0, 0)) {
fprintf(stderr, "daemon: %s\n", strerror(errno));
goto done;
}
msg(LOG_INFO, NULL, "started: %s", rcsid);
umask(0006);
if((pid_fd = fopen(pid_file_name, "w")) == NULL) {
msg(LOG_ERR, NULL, "can't open file: %s", pid_file_name);
goto done;
} else {
pid = getpid();
fprintf(pid_fd, "%d", (int) pid);
fclose(pid_fd);
}
umask(0177);
r = smfi_main();
if (r != MI_SUCCESS)
msg(LOG_ERR, NULL, "smfi_main: terminating due to error");
else
msg(LOG_INFO, NULL, "smfi_main: terminating without error");
done:
return (r);
}
syntax highlighted by Code2HTML, v. 0.9.1