1331722Seadler/*
21590Srgrimes * Copyright (c) 1989, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
3087675Smarkm#include <sys/cdefs.h>
3187675Smarkm
3287675Smarkm__FBSDID("$FreeBSD: stable/11/usr.bin/wall/ttymsg.c 335059 2018-06-13 13:41:23Z ed $");
3387675Smarkm
341590Srgrimes#ifndef lint
3587675Smarkmstatic const char sccsid[] = "@(#)ttymsg.c	8.2 (Berkeley) 11/16/93";
3634800Scharnier#endif
371590Srgrimes
381590Srgrimes#include <sys/types.h>
391590Srgrimes#include <sys/uio.h>
401590Srgrimes#include <dirent.h>
411590Srgrimes#include <errno.h>
4234800Scharnier#include <fcntl.h>
431590Srgrimes#include <paths.h>
4434800Scharnier#include <signal.h>
451590Srgrimes#include <stdio.h>
461590Srgrimes#include <string.h>
47200462Sdelphij#include <stdlib.h>
4834800Scharnier#include <unistd.h>
491590Srgrimes
5083242Sdd#include "ttymsg.h"
5183242Sdd
521590Srgrimes/*
531590Srgrimes * Display the contents of a uio structure on a terminal.  Used by wall(1),
541590Srgrimes * syslogd(8), and talkd(8).  Forks and finishes in child if write would block,
551590Srgrimes * waiting up to tmout seconds.  Returns pointer to error string on unexpected
561590Srgrimes * error; string is not newline-terminated.  Various "normal" errors are
571590Srgrimes * ignored (exclusive-use, lack of permission, etc.).
581590Srgrimes */
5983242Sddconst char *
6073255Simpttymsg(struct iovec *iov, int iovcnt, const char *line, int tmout)
611590Srgrimes{
62335059Sed	struct iovec localiov[TTYMSG_IOV_MAX];
6383242Sdd	ssize_t left, wret;
6483242Sdd	int cnt, fd;
65286268Spfg	char device[MAXNAMLEN] = _PATH_DEV;
661590Srgrimes	static char errbuf[1024];
67154898Sume	char *p;
6873255Simp	int forked;
691590Srgrimes
7073255Simp	forked = 0;
7183242Sdd	if (iovcnt > (int)(sizeof(localiov) / sizeof(localiov[0])))
721590Srgrimes		return ("too many iov's (change code in wall/ttymsg.c)");
731590Srgrimes
74286268Spfg	strlcat(device, line, sizeof(device));
75154898Sume	p = device + sizeof(_PATH_DEV) - 1;
76154898Sume	if (strncmp(p, "pts/", 4) == 0)
77154898Sume		p += 4;
78154898Sume	if (strchr(p, '/') != NULL) {
791590Srgrimes		/* A slash is an attempt to break security... */
8073255Simp		(void) snprintf(errbuf, sizeof(errbuf),
8173255Simp		    "Too many '/' in \"%s\"", device);
821590Srgrimes		return (errbuf);
831590Srgrimes	}
841590Srgrimes
851590Srgrimes	/*
861590Srgrimes	 * open will fail on slip lines or exclusive-use lines
871590Srgrimes	 * if not running as root; not an error.
881590Srgrimes	 */
891590Srgrimes	if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) {
901590Srgrimes		if (errno == EBUSY || errno == EACCES)
911590Srgrimes			return (NULL);
9273255Simp		(void) snprintf(errbuf, sizeof(errbuf), "%s: %s", device,
9373255Simp		    strerror(errno));
941590Srgrimes		return (errbuf);
951590Srgrimes	}
961590Srgrimes
9783242Sdd	for (cnt = 0, left = 0; cnt < iovcnt; ++cnt)
981590Srgrimes		left += iov[cnt].iov_len;
991590Srgrimes
1001590Srgrimes	for (;;) {
1011590Srgrimes		wret = writev(fd, iov, iovcnt);
1021590Srgrimes		if (wret >= left)
1031590Srgrimes			break;
1041590Srgrimes		if (wret >= 0) {
1051590Srgrimes			left -= wret;
1061590Srgrimes			if (iov != localiov) {
10773255Simp				bcopy(iov, localiov,
1081590Srgrimes				    iovcnt * sizeof(struct iovec));
1091590Srgrimes				iov = localiov;
1101590Srgrimes			}
11183242Sdd			for (cnt = 0; (size_t)wret >= iov->iov_len; ++cnt) {
1121590Srgrimes				wret -= iov->iov_len;
1131590Srgrimes				++iov;
1141590Srgrimes				--iovcnt;
1151590Srgrimes			}
1161590Srgrimes			if (wret) {
117104908Smike				iov->iov_base = (char *)iov->iov_base + wret;
1181590Srgrimes				iov->iov_len -= wret;
1191590Srgrimes			}
1201590Srgrimes			continue;
1211590Srgrimes		}
1221590Srgrimes		if (errno == EWOULDBLOCK) {
12361464Sghelmer			int cpid;
1241590Srgrimes
1251590Srgrimes			if (forked) {
1261590Srgrimes				(void) close(fd);
1271590Srgrimes				_exit(1);
1281590Srgrimes			}
1291590Srgrimes			cpid = fork();
1301590Srgrimes			if (cpid < 0) {
1311590Srgrimes				(void) snprintf(errbuf, sizeof(errbuf),
1321590Srgrimes				    "fork: %s", strerror(errno));
1331590Srgrimes				(void) close(fd);
1341590Srgrimes				return (errbuf);
1351590Srgrimes			}
1361590Srgrimes			if (cpid) {	/* parent */
1371590Srgrimes				(void) close(fd);
1381590Srgrimes				return (NULL);
1391590Srgrimes			}
1401590Srgrimes			forked++;
1411590Srgrimes			/* wait at most tmout seconds */
1421590Srgrimes			(void) signal(SIGALRM, SIG_DFL);
1431590Srgrimes			(void) signal(SIGTERM, SIG_DFL); /* XXX */
1441590Srgrimes			(void) sigsetmask(0);
1451590Srgrimes			(void) alarm((u_int)tmout);
14661464Sghelmer			(void) fcntl(fd, F_SETFL, 0);	/* clear O_NONBLOCK */
1471590Srgrimes			continue;
1481590Srgrimes		}
1491590Srgrimes		/*
1501590Srgrimes		 * We get ENODEV on a slip line if we're running as root,
1511590Srgrimes		 * and EIO if the line just went away.
1521590Srgrimes		 */
1531590Srgrimes		if (errno == ENODEV || errno == EIO)
1541590Srgrimes			break;
1551590Srgrimes		(void) close(fd);
1561590Srgrimes		if (forked)
1571590Srgrimes			_exit(1);
1581590Srgrimes		(void) snprintf(errbuf, sizeof(errbuf),
1591590Srgrimes		    "%s: %s", device, strerror(errno));
1601590Srgrimes		return (errbuf);
1611590Srgrimes	}
1621590Srgrimes
1631590Srgrimes	(void) close(fd);
1641590Srgrimes	if (forked)
1651590Srgrimes		_exit(0);
1661590Srgrimes	return (NULL);
1671590Srgrimes}
168