aboutsummaryrefslogtreecommitdiff
path: root/mockmta.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org>2021-06-16 17:22:20 +0300
committerSergey Poznyakoff <gray@gnu.org>2021-06-16 17:26:01 +0300
commitec3c578709f1a25af5fc1b3e9ee50583e1fc4b05 (patch)
tree2fba0a0c2a9f541dc59a51c2522901aa87f7d8bc /mockmta.c
parent30437b7361c5763309639b1bf0aa015e2ad057ed (diff)
downloadmockmta-ec3c578709f1a25af5fc1b3e9ee50583e1fc4b05.tar.gz
mockmta-ec3c578709f1a25af5fc1b3e9ee50583e1fc4b05.tar.bz2
Version 1.0v1.0
* mockmta.c: Implement logging. Write pidfile, if requested. New options: -P, -?, -v. Various bugfixes. * .gitignore: Update * COPYING: New file. * Makefile.am: Distribute COPYING; install mockmail.1 * README: New file. * configure.ac: Organizational changes. * mockmta.1: New file.
Diffstat (limited to 'mockmta.c')
-rw-r--r--mockmta.c536
1 files changed, 375 insertions, 161 deletions
diff --git a/mockmta.c b/mockmta.c
index d2737fd..342f3dc 100644
--- a/mockmta.c
+++ b/mockmta.c
@@ -1,75 +1,18 @@
-/*
- NAME
- mockmta - mock MTA server for use in test suites
-
- SYNOPSIS
- mockmta [-d] [-c CERT] [-a CA] [-k KEY] [-p PORT] [-t SEC] MAILBOX
-
- DESCRIPTION
- Starts a mock MTA, which behaves almost identically to the real one,
- except that it listens on localhost only and delivers all messages
- to the given MAILBOX file.
-
- No attempts are made to interpret the data supplied during the STMP
- transaction, such as domain names, email addresses, etc, neither is
- the material supplied in the DATA command verified to be a valid
- email message. Except for being written to MAILBOX, these data are
- ignored.
-
- Mockmta can work both as a foreground process and as a standalone
- daemon. The foreground mode can be used with GNU pies as follows:
-
- component sm {
- mode inetd;
- socket inet://0.0.0.0:25;
- command "/usr/bin/mockmta /var/spool/mail/dropmail";
- }
-
- When run as a daemon, mockmta starts listening on localhost port
- PORT (default 25).
-
- To support TLS, the program must be compiled with the GnuTLS library.
-
- To enable the STARTTLS ESMTP command, supply the names of the certificate
- (-c CERT) and certificate key (-k KEY) files.
-
- OPTIONS
- -a CA Name of certificate authority file.
- -c CERT Name of the certificate file.
- -d Daemon mode.
- -f Remain in foreground (implies -d).
- -k KEY Name of the certificate key file.
- -p PORT Listen on this port.
- -t SEC Set SMTP timeout.
-
- EXIT CODES
- 0 Success.
- 1 Failure (see stderr for details).
- 2 Timed out waiting for I/O.
- 3 Command line usage error.
-
- BUGS
- At most 32 RCPT commands are allowed.
-
- AUTHOR
- Sergey Poznyakoff <gray@gnu.org>
-
- LICENSE
- Copyright (C) 2020-2021 Free Software Foundation, Inc.
-
- Mockmta 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 3, or (at your option)
- any later version.
-
- Mockmta 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
-
+/* mockmta - mock MTA server for use in test suites
+ * Copyright (C) 2020-2021 Sergey Poznyakoff
+ *
+ * Mockmta 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 3, or (at your option)
+ * any later version.
+ *
+ * Mockmta 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 GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
@@ -88,20 +31,20 @@
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
-#include <paths.h>
+#ifdef HAVE_PATHS_H
+# include <paths.h>
+#endif
+#ifndef _PATH_DEVNULL
+# define _PATH_DEVNULL "/dev/null"
+#endif
char *progname;
char *mailbox_name;
int daemon_opt;
int smtp_timeout = 5*60;
int port = 25;
-
-int msgid = 1;
-pthread_mutex_t msgid_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-#ifndef _PATH_DEVNULL
-# define _PATH_DEVNULL "/dev/null"
-#endif
+int log_level = LOG_INFO;
+char *pidfile;
enum
{
@@ -120,72 +63,193 @@ enum
EX_USAGE
};
+
+#define SID_ABC "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+#define SID_ABC_LEN (sizeof (SID_ABC) - 1)
+#define SID_ABC_LEN_SQR (SID_ABC_LEN * SID_ABC_LEN)
+
+#define SID_BUF_LEN 9
+
+/*
+ * Generate (almost) unique session ID. The idea borrowed from Sendmail
+ * queue ID.
+ */
+int
+generate_sid (char *sidbuf)
+{
+ static int init;
+ static unsigned int seqno = 0;
+ static pthread_mutex_t sid_mutex = PTHREAD_MUTEX_INITIALIZER;
+ static char abc[] = SID_ABC;
+
+ struct timeval t;
+ struct tm *tm;
+ unsigned int n;
+ char *p;
+
+ gettimeofday (&t, NULL);
+
+ pthread_mutex_lock (&sid_mutex);
+ if (!init)
+ {
+ seqno = getpid () + t.tv_sec + t.tv_usec;
+ init = 1;
+ }
+
+ n = seqno++ % SID_ABC_LEN_SQR;
+ pthread_mutex_unlock (&sid_mutex);
+
+ tm = gmtime (&t.tv_sec);
+ sidbuf[0] = abc[tm->tm_year % SID_ABC_LEN];
+ sidbuf[1] = abc[tm->tm_mon];
+ sidbuf[2] = abc[tm->tm_mday];
+ sidbuf[3] = abc[tm->tm_hour];
+ sidbuf[4] = abc[tm->tm_min % SID_ABC_LEN];
+ sidbuf[5] = abc[tm->tm_sec % SID_ABC_LEN];
+ sidbuf[6] = abc[n / SID_ABC_LEN];
+ sidbuf[7] = abc[n % SID_ABC_LEN];
+ sidbuf[8] = 0;
+
+ p = strdup (sidbuf);
+ if (!p)
+ return errno;
+ return 0;
+}
+
+static void (*logger) (int, char const *fmt, ...);
+
+static pthread_key_t sdat_key;
+static pthread_once_t sdat_key_once = PTHREAD_ONCE_INIT;
+
+struct session_data
+{
+ char sid[SID_BUF_LEN];
+ int nomem;
+ char *fmtbuf;
+ size_t fmtsize;
+};
+
+static void
+sdat_free (void *f)
+{
+ free (f);
+}
+
+static void
+make_sdat_key (void)
+{
+ pthread_key_create (&sdat_key, sdat_free);
+}
+
+static struct session_data *
+priv_get_session_data (void)
+{
+ struct session_data *sdat;
+ pthread_once (&sdat_key_once, make_sdat_key);
+ if ((sdat = pthread_getspecific (sdat_key)) == NULL)
+ {
+ sdat = calloc (1, sizeof(*sdat));
+ if (sdat == NULL)
+ logger (LOG_CRIT, "out of memory");
+ pthread_setspecific (sdat_key, sdat);
+ }
+ return sdat;
+}
+
+static void nomemory (void);
+
static void
-terror_stderr (char const *fmt, ...)
+log_stderr (int prio, char const *fmt, ...)
{
va_list ap;
int m;
- static char *fmtbuf = NULL;
- static size_t fmtsize = 0;
int ec = errno;
char const *es = NULL;
size_t len;
+ struct session_data *sd = priv_get_session_data ();
- for (m = 0; fmt[m += strcspn (fmt + m, "%")]; )
- {
- m++;
- if (fmt[m] == 'm')
- break;
- }
-
- len = strlen (fmt) + 1;
- if (fmt[m])
- {
- es = strerror (ec);
- len += strlen (es) - 2;
- }
- if (len > fmtsize)
+ if (prio > log_level)
+ return;
+
+ if (!sd->nomem)
{
- fmtsize = len;
- fmtbuf = realloc (fmtbuf, fmtsize);
- if (!fmtbuf)
+ for (m = 0; fmt[m += strcspn (fmt + m, "%")]; )
{
- perror ("realloc");
- exit (EX_FAILURE);
+ m++;
+ if (fmt[m] == 'm')
+ break;
+ }
+
+ len = strlen (fmt) + 1;
+ if (fmt[m])
+ {
+ es = strerror (ec);
+ len += strlen (es) - 2;
}
- }
- if (es)
- {
- memcpy (fmtbuf, fmt, m - 1);
- memcpy (fmtbuf + m - 1, es, strlen (es) + 1);
- strcat (fmtbuf, fmt + m + 1);
- }
- else
- strcpy (fmtbuf, fmt);
+ if (es)
+ {
+ if (len > sd->fmtsize)
+ {
+ sd->fmtsize = len;
+ sd->fmtbuf = realloc (sd->fmtbuf, sd->fmtsize);
+ if (!sd->fmtbuf)
+ nomemory ();
+ }
+
+ memcpy (sd->fmtbuf, fmt, m - 1);
+ memcpy (sd->fmtbuf + m - 1, es, strlen (es) + 1);
+ strcat (sd->fmtbuf, fmt + m + 1);
+ fmt = sd->fmtbuf;
+ }
+ }
+
va_start (ap, fmt);
fprintf (stderr, "%s: ", progname);
- vfprintf (stderr, fmtbuf, ap);
+ if (sd->sid[0])
+ fprintf (stderr, "%s: ", sd->sid);
+ vfprintf (stderr, fmt, ap);
fputc ('\n', stderr);
va_end (ap);
}
static void
-terror_syslog (char const *fmt, ...)
+log_syslog (int prio, char const *fmt, ...)
{
va_list ap;
+ struct session_data *sd = priv_get_session_data ();
+
+ if (prio > log_level)
+ return;
+
+ if (!sd->nomem && sd->sid[0])
+ {
+ size_t len = strlen (sd->sid) + 2 + strlen (fmt) + 1;
+ if (len > sd->fmtsize)
+ {
+ sd->fmtsize = len;
+ sd->fmtbuf = realloc (sd->fmtbuf, sd->fmtsize);
+ if (!sd->fmtbuf)
+ nomemory ();
+ }
+ snprintf (sd->fmtbuf, sd->fmtsize, "%s: %s", sd->sid, fmt);
+ fmt = sd->fmtbuf;
+ }
+
va_start (ap, fmt);
- vsyslog (LOG_ERR, fmt, ap);
+ vsyslog (prio, fmt, ap);
va_end (ap);
}
-static void (*terror) (char const *fmt, ...) = terror_stderr;
-
+static void (*logger) (int, char const *fmt, ...) = log_stderr;
+
static void
nomemory (void)
{
- terror ("out of memory");
+ struct session_data *sd = priv_get_session_data ();
+ sd->nomem = 1;
+ logger (LOG_CRIT, "out of memory");
exit (EX_FAILURE);
}
@@ -682,7 +746,7 @@ tls_init (void)
GNUTLS_X509_FMT_PEM);
if (rc < 0)
{
- terror ("%s: %s", tls_cafile, gnutls_strerror (rc));
+ logger (LOG_ERR, "%s: %s", tls_cafile, gnutls_strerror (rc));
return -1;
}
}
@@ -692,7 +756,8 @@ tls_init (void)
GNUTLS_X509_FMT_PEM);
if (rc < 0)
{
- terror ("error reading certificate files: %s", gnutls_strerror (rc));
+ logger (LOG_ERR, "error reading certificate files: %s",
+ gnutls_strerror (rc));
return -1;
}
@@ -761,7 +826,8 @@ iotls_create (int in, int out)
}
#else
static inline int set_tls_opt (int c) {
- terror ("option -%c not supported: program compiled without support for TLS",
+ logger (LOG_ERR,
+ "option -%c not supported: program compiled without support for TLS",
c);
return 1;
}
@@ -854,11 +920,21 @@ enum smtp_state
#define MAX_RCPT 32
+static char *state_str[] = {
+ [STATE_ERR] = "error",
+ [STATE_INIT] = "init",
+ [STATE_EHLO] = "ehlo",
+ [STATE_MAIL] = "mail",
+ [STATE_RCPT] = "rcpt",
+ [STATE_DATA] = "data",
+ [STATE_QUIT] = "quit"
+};
+
struct smtp
{
enum smtp_state state;
struct iobase *iob;
- unsigned sid;
+ char sid[SID_BUF_LEN];
char buf[IOBUFSIZE];
char *arg;
int capa_mask;
@@ -866,7 +942,7 @@ struct smtp
char *sender;
char *rcpt[MAX_RCPT];
int nrcpt;
- int tempfd;
+ int delivered;
};
enum
@@ -884,8 +960,6 @@ static char const *capa_str[] = {
};
#define CAPA_MASK(n) (1<<(n))
-
-
struct smtp *
smtp_create (int ifd, int ofd)
@@ -902,11 +976,7 @@ smtp_create (int ifd, int ofd)
smtp->helo = NULL;
smtp->sender = NULL;
smtp->nrcpt = 0;
-
- pthread_mutex_lock (&msgid_mutex);
- smtp->sid = msgid++;
- pthread_mutex_unlock (&msgid_mutex);
-
+ generate_sid (smtp->sid);
return smtp;
}
@@ -937,10 +1007,11 @@ smtp_io_send (struct smtp *smtp, int code, char *fmt, ...)
smtp_timer_enqueue (pthread_self (), SMTP_OUT);
if (iobase_writeln (smtp->iob, buf, n) < 0)
{
- terror ("iobase_writeln: %s", iobase_strerror (smtp->iob));
+ logger (LOG_ERR, "iobase_writeln: %s", iobase_strerror (smtp->iob));
pthread_exit (NULL);
}
smtp_timer_dequeue (pthread_self ());
+ logger (LOG_DEBUG, "S: %.*s", n-2, buf);
}
static void
@@ -958,10 +1029,11 @@ smtp_io_mlsend (struct smtp *smtp, int code, char const **av)
smtp_timer_enqueue (pthread_self (), SMTP_OUT);
if (iobase_writeln (smtp->iob, buf, n) < 0)
{
- terror ("iobase_writeln: %s", iobase_strerror (smtp->iob));
+ logger (LOG_ERR, "iobase_writeln: %s", iobase_strerror (smtp->iob));
pthread_exit (NULL);
}
smtp_timer_dequeue (pthread_self ());
+ logger (LOG_DEBUG, "S: %.*s", n-2, buf);
}
}
@@ -996,7 +1068,6 @@ void
smtp_end (struct smtp *smtp)
{
smtp_io_send (smtp, 221, "Bye");
- smtp_reset (smtp, STATE_INIT);
}
void
@@ -1237,7 +1308,7 @@ mailbox_append (FILE *tf)
fd = open (mailbox_name, O_CREAT|O_WRONLY|O_APPEND, 0600);
if (fd == -1)
{
- terror ("can't open %s: %s", mailbox_name, strerror (errno));
+ logger (LOG_ERR, "can't open %s: %s", mailbox_name, strerror (errno));
return -1;
}
lk.l_type = F_WRLCK;
@@ -1246,7 +1317,7 @@ mailbox_append (FILE *tf)
lk.l_len = 0;
if (fcntl (fd, F_SETLKW, &lk)) /* FIXME: ttl */
{
- terror ("can't lock %s: %s", mailbox_name, strerror (errno));
+ logger (LOG_ERR, "can't lock %s: %s", mailbox_name, strerror (errno));
close (fd);
return -1;
}
@@ -1256,7 +1327,7 @@ mailbox_append (FILE *tf)
fp = fdopen (fd, "a");
if (!fp)
{
- terror ("fdopen: %s", strerror (errno));
+ logger (LOG_ERR, "fdopen: %s", strerror (errno));
close (fd);
return -1;
}
@@ -1276,7 +1347,7 @@ mailbox_append (FILE *tf)
lk.l_start = 0;
lk.l_len = 0;
if (fcntl (fd, F_SETLK, &lk))
- terror ("can't unlock %s: %m", mailbox_name);
+ logger (LOG_ERR, "can't unlock %s: %m", mailbox_name);
fclose (fp);
return res;
@@ -1302,7 +1373,7 @@ smtp_data (struct smtp *smtp)
fd = mkstemp (template);
if (fd == -1)
{
- terror ("can't create temporary: %m");
+ logger (LOG_ERR, "can't create temporary: %m");
smtp_io_send (smtp, 451, "Local filesystem error");
return -1;
}
@@ -1310,7 +1381,7 @@ smtp_data (struct smtp *smtp)
fp = fdopen (fd, "w+");
if (!fp)
{
- terror ("fdopen: %m");
+ logger (LOG_ERR, "fdopen: %m");
smtp_io_send (smtp, 451, "Local filesystem error");
close (fd);
return -1;
@@ -1335,9 +1406,10 @@ smtp_data (struct smtp *smtp)
{
smtp->state = STATE_QUIT;
if (smtp->iob->iob_eof)
- terror ("unexpected end of file");
+ logger (LOG_ERR, "unexpected end of file");
else
- terror ("read error: %s", strerror (smtp->iob->iob_errno));
+ logger (LOG_ERR, "read error: %s",
+ strerror (smtp->iob->iob_errno));
res = 1;
break;
}
@@ -1353,7 +1425,7 @@ smtp_data (struct smtp *smtp)
}
else
{
- terror ("line too long");
+ logger (LOG_ERR, "line too long");
break;
}
@@ -1388,10 +1460,18 @@ smtp_data (struct smtp *smtp)
pthread_cleanup_pop (1);
+ if (smtp->nrcpt > 1)
+ logger (LOG_INFO, "%s => %s (%d recipients) delivered",
+ smtp->sender, smtp->rcpt[0], smtp->nrcpt);
+ else
+ logger (LOG_INFO, "%s => %s delivered",
+ smtp->sender, smtp->rcpt[0]);
+
if (res)
smtp_io_send (smtp, 451, "Local filesystem error");
else
- smtp_io_send (smtp, 250, "%x Message accepted for delivery", smtp->sid);
+ smtp_io_send (smtp, 250, "%s Message accepted for delivery", smtp->sid);
+ smtp->delivered++;
return res;
}
@@ -1453,6 +1533,7 @@ do_smtp (struct smtp *smtp)
{
struct smtp_transition *trans;
+ logger (LOG_DEBUG, "session started");
smtp_io_send (smtp, 220, "Ready");
while (smtp->state != STATE_QUIT)
{
@@ -1463,6 +1544,7 @@ do_smtp (struct smtp *smtp)
if (n <= 0)
break;
smtp->buf[--n] = 0;
+ logger (LOG_DEBUG, "C: %s", smtp->buf);
i = strcspn (smtp->buf, " \t");
if (smtp->buf[i])
{
@@ -1497,6 +1579,9 @@ do_smtp (struct smtp *smtp)
smtp->state = new_state;
}
+ if (!smtp->delivered)
+ logger (LOG_NOTICE, "session ended without delivery");
+ logger (LOG_DEBUG, "session finished");
smtp_end (smtp);
}
@@ -1611,14 +1696,17 @@ thr_watcher (void *ptr)
default:
/* Should not happen */
- terror ("unexpected error from pthread_cond_timedwait: %m");
+ logger (LOG_ERR, "unexpected error from pthread_cond_timedwait: %m");
exit (EX_FAILURE);
}
- /* Thread I/O timed out. Terminate the thread. */
- pthread_cancel (timer->tid);
- smtp_timer_unlink (timer);
- free (timer);
+ if (timer == smtp_timer_head)
+ {
+ /* Thread I/O timed out. Terminate the thread. */
+ pthread_cancel (timer->tid);
+ smtp_timer_unlink (timer);
+ free (timer);
+ }
}
pthread_mutex_unlock (&smtp_timer_mutex);
return NULL;
@@ -1634,7 +1722,7 @@ mta_open (int port)
fd = socket (PF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
- terror ("socket: %m");
+ logger (LOG_ERR, "socket: %m");
exit (EX_FAILURE);
}
@@ -1648,7 +1736,7 @@ mta_open (int port)
if (bind (fd, (struct sockaddr *) &address, sizeof (address)) < 0)
{
close (fd);
- terror ("bind: %m");
+ logger (LOG_ERR, "bind: %m");
exit (EX_FAILURE);
}
@@ -1661,12 +1749,17 @@ static void
smtp_cleanup (void *ptr)
{
struct smtp *smtp = ptr;
+ if (smtp->state != STATE_QUIT)
+ logger (LOG_NOTICE, "session timed out (%s)", state_str[smtp->state]);
smtp_free (smtp);
}
void *
thr_smtp (void *ptr)
{
+ struct smtp *smtp = ptr;
+ struct session_data *sd = priv_get_session_data ();
+ memcpy (sd->sid, smtp->sid, sizeof (sd->sid));
pthread_cleanup_push (smtp_cleanup, ptr);
do_smtp (ptr);
pthread_cleanup_pop (1);
@@ -1689,6 +1782,8 @@ thr_smtp_stdio (void *ptr)
return NULL;
}
+#define MAX_HOST_NAME 256
+
void *
thr_mta_listener (void *ptr)
{
@@ -1708,7 +1803,7 @@ thr_mta_listener (void *ptr)
if ((sfd = accept (fd, (struct sockaddr *) &remote_addr, &len)) < 0)
{
- terror ("accept: %m");
+ logger (LOG_ERR, "accept: %m");
exit (EX_FAILURE);
}
@@ -1731,6 +1826,86 @@ signull(int sig)
{
}
+static void
+set_log_level (char const *arg)
+{
+ static char *logstr[] = {
+ [LOG_EMERG] = "emerg",
+ [LOG_ALERT] = "alert",
+ [LOG_CRIT] = "crit",
+ [LOG_ERR] = "err",
+ [LOG_WARNING] = "warning",
+ [LOG_NOTICE] = "notice",
+ [LOG_INFO] = "info",
+ [LOG_DEBUG] = "debug",
+ NULL
+ };
+ int i;
+
+ for (i = 0; logstr[i]; i++)
+ if (strcmp (logstr[i], arg) == 0)
+ {
+ log_level = i;
+ return;
+ }
+
+ logger (LOG_CRIT, "invalid log level: %s", arg);
+ exit (EX_USAGE);
+}
+
+static char usage_help[] = "Usage: mockmta [OPTIONS] MAILBOX";
+static char prog_descr[] = "Simple MTA for testing purposes";
+static char *opthelp[] = {
+#ifdef WITH_TLS
+ "-a CA name of certificate authority file",
+ "-c CERT name of the certificate file",
+#endif
+ "-d daemon mode",
+ "-f remain in foreground (implies -d)",
+#ifdef WITH_TLS
+ "-k KEY name of the certificate key file",
+#endif
+ "-l LEVEL log events at the specified, or more important, loglevels",
+ "-P FILE write PID to FILE (with -d or -f)",
+ "-p PORT listen on this port",
+ "-t SEC set SMTP timeout",
+ "-v print program version and exit",
+ "-? print this help summary and exit",
+ NULL
+};
+
+static void
+usage (FILE *fp)
+{
+ int i;
+
+ fprintf (fp, "%s\n", usage_help);
+ fprintf (fp, "%s\n", prog_descr);
+ fprintf (fp, "\nOPTIONS are:\n\n");
+ for (i = 0; opthelp[i]; i++)
+ fprintf (fp, " %s\n", opthelp[i]);
+ fputc ('\n', fp);
+ fprintf (fp, "Report bugs to <%s>\n", PACKAGE_BUGREPORT);
+ fprintf (fp, "%s home page: <%s>\n", PACKAGE_NAME, PACKAGE_URL);
+}
+
+static int copyright_year = 2021;
+
+static void
+version (void)
+{
+ printf ("%s (%s) %s\n", progname, PACKAGE_NAME, PACKAGE_VERSION);
+ printf ("Copyright (C) %d Sergey Poznyakoff\n", copyright_year);
+ printf ("\
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\nThis is free software: you are free to change and redistribute it.\n\
+There is NO WARRANTY, to the extent permitted by law.\n\
+");
+#ifdef WITH_TLS
+ printf ("\n");
+ printf ("Using GnuTLS %s\n", gnutls_check_version (NULL));
+#endif
+}
+
int
main (int argc, char **argv)
{
@@ -1744,7 +1919,7 @@ main (int argc, char **argv)
progname = argv[0];
- while ((c = getopt (argc, argv, "a:dc:fk:p:t:")) != EOF)
+ while ((c = getopt (argc, argv, "a:dc:fk:l:P:p:t:v?")) != EOF)
{
switch (c)
{
@@ -1756,6 +1931,14 @@ main (int argc, char **argv)
daemon_opt = 1;
foreground = 1;
break;
+
+ case 'l':
+ set_log_level (optarg);
+ break;
+
+ case 'P':
+ pidfile = optarg;
+ break;
case 'p':
port = atoi (optarg);
@@ -1765,14 +1948,26 @@ main (int argc, char **argv)
smtp_timeout = atoi (optarg);
if (smtp_timeout <= 0)
{
- terror ("invalid timeout value");
+ logger (LOG_ERR, "invalid timeout value");
exit (EX_USAGE);
}
break;
+
+ case 'v':
+ version ();
+ exit (EX_OK);
default:
- if (set_tls_opt (c))
- exit (EX_USAGE);
+ if (optopt == '?')
+ {
+ usage (stdout);
+ exit (EX_OK);
+ }
+ else if (set_tls_opt (c))
+ {
+ usage (stderr);
+ exit (EX_USAGE);
+ }
}
}
@@ -1781,7 +1976,7 @@ main (int argc, char **argv)
if (argc != 1)
{
- terror ("bad number of arguments");
+ usage (stderr);
exit (EX_USAGE);
}
@@ -1809,8 +2004,6 @@ main (int argc, char **argv)
sigaddset (&sigs, SIGCHLD);
pthread_sigmask (SIG_BLOCK, &sigs, NULL);
- pthread_create (&tid, NULL, thr_watcher, NULL);
-
if (daemon_opt)
{
fd = mta_open (port);
@@ -1820,7 +2013,7 @@ main (int argc, char **argv)
switch (fork ())
{
case -1:
- terror ("daemon: %m");
+ logger (LOG_ERR, "daemon: %m");
exit (EX_FAILURE);
case 0:
@@ -1832,7 +2025,7 @@ main (int argc, char **argv)
if (setsid () == -1)
{
- terror ("setsid: %m");
+ logger (LOG_ERR, "setsid: %m");
exit (EX_FAILURE);
}
@@ -1847,13 +2040,31 @@ main (int argc, char **argv)
/* Set up logging */
openlog (progname, LOG_PID, LOG_MAIL);
- terror = terror_syslog;
+ logger = log_syslog;
+
+ if (pidfile)
+ {
+ FILE *fp = fopen (pidfile, "w");
+ if (fp)
+ {
+ fprintf (fp, "%lu\n", (unsigned long) getpid());
+ fclose (fp);
+ }
+ else
+ {
+ logger (LOG_ERR, "can't open pidfile %s for writing: %m",
+ pidfile);
+ pidfile = NULL;
+ }
+ }
}
+ pthread_create (&tid, NULL, thr_watcher, NULL);
pthread_create (&tid, NULL, thr_mta_listener, &fd);
}
else
{
+ pthread_create (&tid, NULL, thr_watcher, NULL);
pthread_create (&tid, NULL, thr_smtp_stdio, smtp_create (0, 1));
pthread_detach (tid);
}
@@ -1867,6 +2078,9 @@ main (int argc, char **argv)
/* Wait for signal to arrive */
sigwait (&sigs, &i);
-
+
+ if (pidfile)
+ unlink (pidfile);
+
exit (EX_OK);
}

Return to:

Send suggestions and report system problems to the System administrator.