git: 12666df00a74 - stable/14 - mail: Don't trap signals we shouldn't.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 21 May 2025 17:47:36 UTC
The branch stable/14 has been updated by des:
URL: https://cgit.FreeBSD.org/src/commit/?id=12666df00a745c64d65c7248f278001b9dbfd937
commit 12666df00a745c64d65c7248f278001b9dbfd937
Author: Dag-Erling Smørgrav <des@FreeBSD.org>
AuthorDate: 2025-04-27 06:29:20 +0000
Commit: Dag-Erling Smørgrav <des@FreeBSD.org>
CommitDate: 2025-05-21 17:33:08 +0000
mail: Don't trap signals we shouldn't.
When in interactive mode, trap SIGINT, SIGHUP, and tty-related signals.
Otherwise, leave signals untouched as required by POSIX.
MFC after: 1 week
Sponsored by: Klara, Inc.
Reviewed by: kevans
Differential Revision: https://reviews.freebsd.org/D50011
(cherry picked from commit 59597032c948586257f123f57bbcfbad02a2cd1b)
mail: Exit non-zero on failure to collect mail.
While here, avoid non-portable `pipe2()` in the regression test.
Fixes: 59597032c948
MFC after: 1 week
Sponsored by: Klara, Inc.
Reviewed by: kevans
Differential Revision: https://reviews.freebsd.org/D50069
(cherry picked from commit 09bc6a5d5b3333bc91e5f9fdf1e7bb282c4aea5a)
mail: Further refine the SIGINT test.
* Wait at least 1-2 s before sending the second SIGINT.
* If the child is still running after 15 s, send a SIGKILL.
* Improve the exit status checks.
Fixes: 59597032c948
MFC after: 1 week
Sponsored by: Klara, Inc.
Reviewed by: markj
Differential Revision: https://reviews.freebsd.org/D50092
(cherry picked from commit 42fd47ef81138ae53ba77add5a400f67af8e6263)
---
etc/mtree/BSD.tests.dist | 2 +
usr.bin/mail/Makefile | 4 +
usr.bin/mail/collect.c | 34 +++++---
usr.bin/mail/send.c | 4 +-
usr.bin/mail/tests/Makefile | 4 +
usr.bin/mail/tests/mail_sigint_test.c | 157 ++++++++++++++++++++++++++++++++++
6 files changed, 190 insertions(+), 15 deletions(-)
diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist
index a909ea3714d3..bf327b14c306 100644
--- a/etc/mtree/BSD.tests.dist
+++ b/etc/mtree/BSD.tests.dist
@@ -1109,6 +1109,8 @@
..
m4
..
+ mail
+ ..
mkimg
..
mktemp
diff --git a/usr.bin/mail/Makefile b/usr.bin/mail/Makefile
index e5b0d8b3a3a6..bbbcf6c892b1 100644
--- a/usr.bin/mail/Makefile
+++ b/usr.bin/mail/Makefile
@@ -1,4 +1,5 @@
# @(#)Makefile 8.2 (Berkeley) 1/25/94
+.include <src.opts.mk>
CONFS= misc/mail.rc
PROG= mail
@@ -19,4 +20,7 @@ etc-mailrc:
cd ${.CURDIR}/misc; ${INSTALL} -o root -g wheel \
-m 644 ${EFILES} ${DESTDIR}/etc
+HAS_TESTS=
+SUBDIR.${MK_TESTS}= tests
+
.include <bsd.prog.mk>
diff --git a/usr.bin/mail/collect.c b/usr.bin/mail/collect.c
index ccb3b60bec2b..e6a6337bcdb0 100644
--- a/usr.bin/mail/collect.c
+++ b/usr.bin/mail/collect.c
@@ -85,16 +85,20 @@ collect(struct header *hp, int printheaders)
* until we're in the main loop.
*/
(void)sigemptyset(&nset);
- (void)sigaddset(&nset, SIGINT);
- (void)sigaddset(&nset, SIGHUP);
+ if (value("interactive") != NULL) {
+ (void)sigaddset(&nset, SIGINT);
+ (void)sigaddset(&nset, SIGHUP);
+ }
(void)sigprocmask(SIG_BLOCK, &nset, NULL);
- if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
- (void)signal(SIGINT, collint);
- if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
- (void)signal(SIGHUP, collhup);
- savetstp = signal(SIGTSTP, collstop);
- savettou = signal(SIGTTOU, collstop);
- savettin = signal(SIGTTIN, collstop);
+ if (value("interactive") != NULL) {
+ if ((saveint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
+ (void)signal(SIGINT, collint);
+ if ((savehup = signal(SIGHUP, SIG_IGN)) != SIG_IGN)
+ (void)signal(SIGHUP, collhup);
+ savetstp = signal(SIGTSTP, collstop);
+ savettou = signal(SIGTTOU, collstop);
+ savettin = signal(SIGTTIN, collstop);
+ }
if (setjmp(collabort) || setjmp(colljmp)) {
(void)rm(tempname);
goto err;
@@ -479,11 +483,13 @@ out:
rewind(collf);
noreset--;
(void)sigprocmask(SIG_BLOCK, &nset, NULL);
- (void)signal(SIGINT, saveint);
- (void)signal(SIGHUP, savehup);
- (void)signal(SIGTSTP, savetstp);
- (void)signal(SIGTTOU, savettou);
- (void)signal(SIGTTIN, savettin);
+ if (value("interactive") != NULL) {
+ (void)signal(SIGINT, saveint);
+ (void)signal(SIGHUP, savehup);
+ (void)signal(SIGTSTP, savetstp);
+ (void)signal(SIGTTOU, savettou);
+ (void)signal(SIGTTIN, savettin);
+ }
(void)sigprocmask(SIG_UNBLOCK, &nset, NULL);
return (collf);
}
diff --git a/usr.bin/mail/send.c b/usr.bin/mail/send.c
index 002157ba55cb..395b42c1e749 100644
--- a/usr.bin/mail/send.c
+++ b/usr.bin/mail/send.c
@@ -299,8 +299,10 @@ mail1(struct header *hp, int printheaders)
* Collect user's mail from standard input.
* Get the result as mtf.
*/
- if ((mtf = collect(hp, printheaders)) == NULL)
+ if ((mtf = collect(hp, printheaders)) == NULL) {
+ senderr++;
return;
+ }
if (value("interactive") != NULL) {
if (value("askcc") != NULL || value("askbcc") != NULL) {
if (value("askcc") != NULL)
diff --git a/usr.bin/mail/tests/Makefile b/usr.bin/mail/tests/Makefile
new file mode 100644
index 000000000000..cb194b3da1f1
--- /dev/null
+++ b/usr.bin/mail/tests/Makefile
@@ -0,0 +1,4 @@
+PACKAGE= tests
+ATF_TESTS_C+= mail_sigint_test
+
+.include <bsd.test.mk>
diff --git a/usr.bin/mail/tests/mail_sigint_test.c b/usr.bin/mail/tests/mail_sigint_test.c
new file mode 100644
index 000000000000..ace5f714c459
--- /dev/null
+++ b/usr.bin/mail/tests/mail_sigint_test.c
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 2025 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/poll.h>
+#include <sys/wait.h>
+
+#include <fcntl.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#define MAILX "mailx"
+#define BODY "hello\n"
+#define BODYLEN (sizeof(BODY) - 1)
+
+/*
+ * When interactive, mailx(1) should print a message on receipt of SIGINT,
+ * then exit cleanly on receipt of a second.
+ *
+ * When not interactive, mailx(1) should terminate on receipt of SIGINT.
+ */
+static void
+mailx_sigint(bool interactive)
+{
+ char obuf[1024] = "";
+ char ebuf[1024] = "";
+ struct pollfd fds[2];
+ int ipd[2], opd[2], epd[2], spd[2];
+ time_t start, now;
+ size_t olen = 0, elen = 0;
+ ssize_t rlen;
+ pid_t pid;
+ int kc, status;
+
+ /* input, output, error, sync pipes */
+ if (pipe(ipd) != 0 || pipe(opd) != 0 || pipe(epd) != 0 ||
+ pipe(spd) != 0 || fcntl(spd[1], F_SETFD, FD_CLOEXEC) != 0)
+ atf_tc_fail("failed to pipe");
+ /* fork child */
+ if ((pid = fork()) < 0)
+ atf_tc_fail("failed to fork");
+ if (pid == 0) {
+ /* child */
+ dup2(ipd[0], STDIN_FILENO);
+ close(ipd[0]);
+ close(ipd[1]);
+ dup2(opd[1], STDOUT_FILENO);
+ close(opd[0]);
+ close(opd[1]);
+ dup2(epd[1], STDERR_FILENO);
+ close(epd[0]);
+ close(epd[1]);
+ close(spd[0]);
+ /* force dead.letter to go to cwd */
+ setenv("HOME", ".", 1);
+ /* exec mailx */
+ execlp(MAILX,
+ MAILX,
+ interactive ? "-Is" : "-s",
+ "test",
+ "test@example.com",
+ NULL);
+ _exit(2);
+ }
+ /* parent */
+ close(ipd[0]);
+ close(opd[1]);
+ close(epd[1]);
+ close(spd[1]);
+ /* block until child execs or exits */
+ (void)read(spd[0], &spd[1], sizeof(spd[1]));
+ /* send one line of input */
+ ATF_REQUIRE_INTEQ(BODYLEN, write(ipd[1], BODY, BODYLEN));
+ /* give it a chance to process */
+ poll(NULL, 0, 2000);
+ /* send first SIGINT */
+ ATF_CHECK_INTEQ(0, kill(pid, SIGINT));
+ kc = 1;
+ /* receive output until child terminates */
+ fds[0].fd = opd[0];
+ fds[0].events = POLLIN;
+ fds[1].fd = epd[0];
+ fds[1].events = POLLIN;
+ time(&start);
+ for (;;) {
+ ATF_REQUIRE(poll(fds, 2, 1000) >= 0);
+ if (fds[0].revents == POLLIN && olen < sizeof(obuf)) {
+ rlen = read(opd[0], obuf + olen, sizeof(obuf) - olen - 1);
+ ATF_REQUIRE(rlen >= 0);
+ olen += rlen;
+ }
+ if (fds[1].revents == POLLIN && elen < sizeof(ebuf)) {
+ rlen = read(epd[0], ebuf + elen, sizeof(ebuf) - elen - 1);
+ ATF_REQUIRE(rlen >= 0);
+ elen += rlen;
+ }
+ time(&now);
+ if (now - start > 1 && elen > 0 && kc == 1) {
+ ATF_CHECK_INTEQ(0, kill(pid, SIGINT));
+ kc++;
+ }
+ if (now - start > 15 && kc > 0) {
+ (void)kill(pid, SIGKILL);
+ kc = -1;
+ }
+ if (waitpid(pid, &status, WNOHANG) == pid)
+ break;
+ }
+ close(ipd[1]);
+ close(opd[0]);
+ close(epd[0]);
+ close(spd[0]);
+ if (interactive) {
+ ATF_CHECK(WIFEXITED(status));
+ if (WIFEXITED(status))
+ ATF_CHECK_INTEQ(1, WEXITSTATUS(status));
+ ATF_CHECK_INTEQ(2, kc);
+ ATF_CHECK_STREQ("", obuf);
+ ATF_CHECK_MATCH("Interrupt -- one more to kill letter", ebuf);
+ atf_utils_compare_file("dead.letter", BODY);
+ } else {
+ ATF_CHECK(WIFSIGNALED(status));
+ if (WIFSIGNALED(status))
+ ATF_CHECK_INTEQ(SIGINT, WTERMSIG(status));
+ ATF_CHECK_INTEQ(1, kc);
+ ATF_CHECK_STREQ("", obuf);
+ ATF_CHECK_STREQ("", ebuf);
+ ATF_CHECK_INTEQ(-1, access("dead.letter", F_OK));
+ }
+}
+
+
+ATF_TC_WITHOUT_HEAD(mail_sigint_interactive);
+ATF_TC_BODY(mail_sigint_interactive, tc)
+{
+ mailx_sigint(true);
+}
+
+ATF_TC_WITHOUT_HEAD(mail_sigint_noninteractive);
+ATF_TC_BODY(mail_sigint_noninteractive, tc)
+{
+ mailx_sigint(false);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, mail_sigint_interactive);
+ ATF_TP_ADD_TC(tp, mail_sigint_noninteractive);
+ return (atf_no_error());
+}