/* Copyright (c) The Exim Maintainers 2020 - 2022 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Functions concerned with running Exim as a daemon */
static BOOL write_pid = TRUE;
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+static uschar * notifier_socket_name;
+#endif
/*************************************************
/*************************************************
*************************************************/
-#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
static void
unlink_notifier_socket(void)
{
-uschar * s = expand_string(notifier_socket);
-DEBUG(D_any) debug_printf("unlinking notifier socket %s\n", s);
-Uunlink(s);
-}
+#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+DEBUG(D_any) debug_printf("unlinking notifier socket %s\n", notifier_socket_name);
+Uunlink(notifier_socket_name);
#endif
+}
static void
{
(void) close(daemon_notifier_fd);
daemon_notifier_fd = -1;
-#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
- unlink_notifier_socket();
-#endif
}
for (int i = 0; i < listen_socket_count; i++) (void) close(fd_polls[i].fd);
{
close(daemon_notifier_fd);
daemon_notifier_fd = -1;
-#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
unlink_notifier_socket();
-#endif
}
if (f.running_in_test_harness || write_pid)
* Listener socket for local work prompts *
*************************************************/
+ssize_t
+daemon_client_sockname(struct sockaddr_un * sup, uschar ** sname)
+{
+#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+sup->sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */
+return offsetof(struct sockaddr_un, sun_path) + 1
+ + snprintf(sup->sun_path+1, sizeof(sup->sun_path)-1, "exim_%d", getpid());
+#else
+*sname = string_sprintf("%s/p_%d", spool_directory, getpid());
+return offsetof(struct sockaddr_un, sun_path)
+ + snprintf(sup->sun_path, sizeof(sup->sun_path), "%s", CS *sname);
+#endif
+}
+
+ssize_t
+daemon_notifier_sockname(struct sockaddr_un * sup)
+{
+#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
+sup->sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */
+return offsetof(struct sockaddr_un, sun_path) + 1
+ + snprintf(sup->sun_path+1, sizeof(sup->sun_path)-1, "%s",
+ CS expand_string(notifier_socket));
+#else
+notifier_socket_name = expand_string(notifier_socket);
+return offsetof(struct sockaddr_un, sun_path)
+ + snprintf(sup->sun_path, sizeof(sup->sun_path), "%s",
+ CS notifier_socket_name);
+#endif
+}
+
+
static void
daemon_notifier_socket(void)
{
int fd;
const uschar * where;
struct sockaddr_un sa_un = {.sun_family = AF_UNIX};
-int len;
+ssize_t len;
if (!notifier_socket || !*notifier_socket)
{
(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
#endif
+len = daemon_notifier_sockname(&sa_un);
+
#ifdef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS
-sa_un.sun_path[0] = 0; /* Abstract local socket addr - Linux-specific? */
-len = offsetof(struct sockaddr_un, sun_path) + 1
- + snprintf(sa_un.sun_path+1, sizeof(sa_un.sun_path)-1, "%s",
- expand_string(notifier_socket));
DEBUG(D_any) debug_printf(" @%s\n", sa_un.sun_path+1);
#else /* filesystem-visible and persistent; will neeed removal */
-len = offsetof(struct sockaddr_un, sun_path)
- + snprintf(sa_un.sun_path, sizeof(sa_un.sun_path), "%s",
- expand_string(notifier_socket));
DEBUG(D_any) debug_printf(" %s\n", sa_un.sun_path);
#endif
-if (bind(fd, (const struct sockaddr *)&sa_un, len) < 0)
+if (bind(fd, (const struct sockaddr *)&sa_un, (socklen_t)len) < 0)
{ where = US"bind"; goto bad; }
#ifdef SO_PASSCRED /* Linux */
static uschar queuerun_msgid[MESSAGE_ID_LENGTH+1];
-/* Return TRUE if a sigalrm should be emulated */
+/* The notifier socket has something to read. Pull the message from it, decode
+and do the action.
+
+Return TRUE if a sigalrm should be emulated */
+
static BOOL
daemon_notification(void)
{
#ifdef notdef
debug_printf("addrlen %d\n", msg.msg_namelen);
#endif
-DEBUG(D_queue_run) debug_printf("%s from addr '%s%.*s'\n", __FUNCTION__,
- *sa_un.sun_path ? "" : "@",
- (int)msg.msg_namelen - (*sa_un.sun_path ? 0 : 1),
- sa_un.sun_path + (*sa_un.sun_path ? 0 : 1));
+DEBUG(D_queue_run)
+ if (msg.msg_namelen > 0)
+ {
+ BOOL abstract = !*sa_un.sun_path;
+ char * name = sa_un.sun_path + (abstract ? 1 : 0);
+ int namelen = (int)msg.msg_namelen - abstract ? 1 : 0;
+ if (*name)
+ debug_printf("%s from addr '%s%.*s'\n", __FUNCTION__,
+ abstract ? "@" : "",
+ namelen, name);
+ else
+ debug_printf("%s (from unknown addr)\n", __FUNCTION__);
+ }
+ else
+ debug_printf("%s (from unknown addr)\n", __FUNCTION__);
/* Refuse to handle the item unless the peer has good credentials */
#ifdef SCM_CREDENTIALS
{
DEBUG(D_queue_run) debug_printf("%s: sender creds pid %d uid %d gid %d\n",
__FUNCTION__, (int)cr->pid, (int)cr->uid, (int)cr->gid);
- return FALSE;
}
# elif defined(LOCAL_CREDS) /* BSD-ish */
struct sockcred * cr = (struct sockcred *) CMSG_DATA(cp);
{
DEBUG(D_queue_run) debug_printf("%s: sender creds pid ??? uid %d gid %d\n",
__FUNCTION__, (int)cr->sc_uid, (int)cr->sc_gid);
- return FALSE;
}
# endif
break;
(const struct sockaddr *)&sa_un, msg.msg_namelen) < 0)
log_write(0, LOG_MAIN|LOG_PANIC,
"%s: sendto: %s\n", __FUNCTION__, strerror(errno));
- return FALSE;
+ break;
}
+
+ case NOTIFY_REGEX:
+ regex_at_daemon(buf);
+ break;
}
return FALSE;
}
-
/*************************************************
* Exim Daemon Mainline *
*************************************************/
daemon as the result of a SIGHUP. In this case, there is no need to do
anything, because the controlling terminal has long gone. Otherwise, fork, in
case current process is a process group leader (see 'man setsid' for an
- explanation) before calling setsid(). */
+ explanation) before calling setsid().
+ All other forks want daemon_listen cleared. Rather than blow a register, jsut
+ restore it here. */
if (getppid() != 1)
{
+ BOOL daemon_listen = f.daemon_listen;
pid_t pid = exim_fork(US"daemon");
if (pid < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE,
"fork() failed when starting daemon: %s", strerror(errno));
if (pid > 0) exit(EXIT_SUCCESS); /* in parent process, just exit */
(void)setsid(); /* release controlling terminal */
+ f.daemon_listen = daemon_listen;
}
}
if (*--p == '}') *p = '\0'; /* drop EOL */
while (isdigit(*--p)) ; /* char before port */
- i2->log = *p == ':' /* no list yet? */
+ i2->log = *p == ':' /* no list yet? { */
? string_sprintf("%.*s{%s,%d}",
(int)(p - i2->log + 1), i2->log, p+1, ipa->port)
: string_sprintf("%s,%d}", i2->log, ipa->port);
if (p->revents & POLLIN)
{
EXIM_SOCKLEN_T alen = sizeof(accepted);
-#ifdef TCP_INFO
+#if defined(__FreeBSD__) && defined(SO_LISTENQLEN)
+ int backlog;
+ socklen_t blen = sizeof(backlog);
+
+ if ( smtp_backlog_monitor > 0
+ && getsockopt(p->fd, SOL_SOCKET, SO_LISTENQLEN, &backlog, &blen) == 0)
+ {
+ DEBUG(D_interface)
+ debug_printf("listen fd %d queue curr %d\n", p->fd, backlog);
+ smtp_listen_backlog = backlog;
+ }
+
+#elif defined(TCP_INFO) && defined(EXIM_HAVE_TCPI_UNACKED)
struct tcp_info ti;
socklen_t tlen = sizeof(ti);
if ( smtp_backlog_monitor > 0
&& getsockopt(p->fd, IPPROTO_TCP, TCP_INFO, &ti, &tlen) == 0)
{
-# ifdef EXIM_HAVE_TCPI_UNACKED
DEBUG(D_interface) debug_printf("listen fd %d queue max %u curr %u\n",
p->fd, ti.tcpi_sacked, ti.tcpi_unacked);
smtp_listen_backlog = ti.tcpi_unacked;
-# elif defined(__FreeBSD__) /* This does not work. Investigate kernel sourcecode. */
- DEBUG(D_interface) debug_printf("listen fd %d queue max %u curr %u\n",
- p->fd, ti.__tcpi_sacked, ti.__tcpi_unacked);
- smtp_listen_backlog = ti.__tcpi_unacked;
-# endif
}
#endif
p->revents = 0;
log_write(0, LOG_MAIN, "pid %d: SIGHUP received: re-exec daemon",
getpid());
close_daemon_sockets(daemon_notifier_fd, fd_polls, listen_socket_count);
+ unlink_notifier_socket();
ALARM_CLR(0);
signal(SIGHUP, SIG_IGN);
sighup_argv[0] = exim_path;