git: 59597032c948 - main - mail: Don't trap signals we shouldn't.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 27 Apr 2025 06:29:54 UTC
The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=59597032c948586257f123f57bbcfbad02a2cd1b commit 59597032c948586257f123f57bbcfbad02a2cd1b 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-04-27 06:29:32 +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 --- etc/mtree/BSD.tests.dist | 2 + usr.bin/mail/Makefile | 5 ++ usr.bin/mail/collect.c | 34 ++++---- usr.bin/mail/tests/Makefile | 4 + usr.bin/mail/tests/mail_sigint_test.c | 149 ++++++++++++++++++++++++++++++++++ 5 files changed, 180 insertions(+), 14 deletions(-) diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist index 20744e7b944a..673452434080 100644 --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -1165,6 +1165,8 @@ .. m4 .. + mail + .. mkimg .. mktemp diff --git a/usr.bin/mail/Makefile b/usr.bin/mail/Makefile index c20060d88ebf..0a27b5c5a637 100644 --- a/usr.bin/mail/Makefile +++ b/usr.bin/mail/Makefile @@ -1,3 +1,5 @@ +.include <src.opts.mk> + CONFS= misc/mail.rc PROG= mail SRCS= version.c cmd1.c cmd2.c cmd3.c cmdtab.c collect.c edit.c fio.c \ @@ -17,4 +19,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 0c310b15b234..2423bcf7b262 100644 --- a/usr.bin/mail/collect.c +++ b/usr.bin/mail/collect.c @@ -79,16 +79,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; @@ -473,11 +477,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/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..76bf9b19b7f1 --- /dev/null +++ b/usr.bin/mail/tests/mail_sigint_test.c @@ -0,0 +1,149 @@ +/*- + * 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 <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]; + 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 || + pipe2(spd, O_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]); + /* 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; + for (;;) { + if (poll(fds, 2, 1000) < 0) + atf_tc_fail("failed to poll"); + if (fds[0].revents == POLLIN && olen < sizeof(obuf)) { + rlen = read(opd[0], obuf + olen, sizeof(obuf) - olen - 1); + if (rlen < 0) + atf_tc_fail("failed to read"); + olen += rlen; + } + if (fds[1].revents == POLLIN && elen < sizeof(ebuf)) { + rlen = read(epd[0], ebuf + elen, sizeof(ebuf) - elen - 1); + if (rlen < 0) + atf_tc_fail("failed to read"); + elen += rlen; + } + if (elen > 0 && kc == 1) { + kill(pid, SIGINT); + kc++; + } + 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)); + ATF_CHECK_INTEQ(0, 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)); + 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()); +}