tee: try opening as a UNIX socket if open(2) fails

If we get EOPNOTSUPP from open() failing, then it may just be that we're
looking at a unix(4) socket instead of a normal file/device or whatnot.
Fallback to trying to open it as a unix(4) socket, which is a useful
feature that doesn't add much complexity.

Reviewed by:	des, emaste, markj
Differential Revision:	https://reviews.freebsd.org/D48197
This commit is contained in:
Kyle Evans 2025-04-20 11:34:52 -05:00
parent 414c2b8d1e
commit 1b3748977f
3 changed files with 83 additions and 3 deletions

View file

@ -28,7 +28,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd October 30, 2022
.Dd December 25, 2024
.Dt TEE 1
.Os
.Sh NAME
@ -69,6 +69,12 @@ utility takes the default action for all signals,
except in the event of the
.Fl i
option.
.Pp
This implementation of the
.Nm
utility may also write to
.Xr unix 4
sockets.
.Sh EXIT STATUS
.Ex -std
.Sh EXAMPLES

View file

@ -29,10 +29,12 @@
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/capsicum.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <capsicum_helpers.h>
#include <err.h>
@ -52,6 +54,7 @@ struct entry {
static STAILQ_HEAD(, entry) head = STAILQ_HEAD_INITIALIZER(head);
static void add(int, const char *);
static int tee_open(const char *, int);
static void usage(void) __dead2;
int
@ -93,7 +96,7 @@ main(int argc, char *argv[])
oflags |= O_TRUNC;
for (exitval = 0; *argv; ++argv) {
if ((fd = open(*argv, oflags, DEFFILEMODE)) < 0) {
if ((fd = tee_open(*argv, oflags)) < 0) {
warn("%s", *argv);
exitval = 1;
} else {
@ -149,3 +152,42 @@ add(int fd, const char *name)
p->name = name;
STAILQ_INSERT_HEAD(&head, p, entries);
}
static int
tee_open(const char *path, int oflags)
{
struct sockaddr_un sun = { .sun_family = AF_UNIX };
size_t pathlen;
int fd;
if ((fd = open(path, oflags, DEFFILEMODE)) >= 0)
return (fd);
if (errno != EOPNOTSUPP)
return (-1);
pathlen = strnlen(path, sizeof(sun.sun_path));
if (pathlen >= sizeof(sun.sun_path))
goto failed;
/*
* For EOPNOTSUPP, we'll try again as a unix(4) socket. Any errors here
* we'll just surface as the original EOPNOTSUPP since they may not have
* intended for this.
*/
fd = socket(PF_UNIX, SOCK_STREAM, 0);
if (fd < 0)
goto failed;
(void)strlcpy(&sun.sun_path[0], path, sizeof(sun.sun_path));
sun.sun_len = SUN_LEN(&sun);
if (connect(fd, (const struct sockaddr *)&sun, sun.sun_len) == 0)
return (fd);
failed:
if (fd >= 0)
close(fd);
errno = EOPNOTSUPP;
return (-1);
}

View file

@ -64,6 +64,37 @@ sigint_ignored_body()
atf_check -o inline:"text\ntext\n" cat file
}
atf_test_case unixsock "cleanup"
unixsock_pidfile="nc.pid"
unixsock_body()
{
outfile=out.log
nc -lU logger.sock > "$outfile" &
npid=$!
atf_check -o save:"$unixsock_pidfile" echo "$npid"
# Wait for the socket to come online, just in case.
while [ ! -S logger.sock ]; do
sleep 0.1
done
atf_check -o inline:"text over socket\n" -x \
'echo "text over socket" | tee logger.sock'
atf_check rm "$unixsock_pidfile"
atf_check -o inline:"text over socket\n" cat "$outfile"
}
unixsock_cleanup()
{
if [ -s "$unixsock_pidfile" ]; then
read npid < "$unixsock_pidfile"
kill "$npid"
fi
}
atf_init_test_cases()
{
atf_add_test_case single_file
@ -71,4 +102,5 @@ atf_init_test_cases()
atf_add_test_case multiple_file
atf_add_test_case append
atf_add_test_case sigint_ignored
atf_add_test_case unixsock
}