1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 33 #ifndef lint 34 static const char sccsid[] = "@(#)ttymsg.c 8.2 (Berkeley) 11/16/93"; 35 #endif 36 37 #include <sys/types.h> 38 #include <sys/uio.h> 39 #include <dirent.h> 40 #include <errno.h> 41 #include <fcntl.h> 42 #include <paths.h> 43 #include <signal.h> 44 #include <stdio.h> 45 #include <string.h> 46 #include <stdlib.h> 47 #include <unistd.h> 48 49 #include "ttymsg.h" 50 51 /* 52 * Display the contents of a uio structure on a terminal. Used by wall(1), 53 * syslogd(8), and talkd(8). Forks and finishes in child if write would block, 54 * waiting up to tmout seconds. Returns pointer to error string on unexpected 55 * error; string is not newline-terminated. Various "normal" errors are 56 * ignored (exclusive-use, lack of permission, etc.). 57 */ 58 const char * 59 ttymsg(struct iovec *iov, int iovcnt, const char *line, int tmout) 60 { 61 struct iovec localiov[TTYMSG_IOV_MAX]; 62 ssize_t left, wret; 63 int cnt, fd; 64 char device[MAXNAMLEN] = _PATH_DEV; 65 static char errbuf[1024]; 66 char *p; 67 int forked; 68 69 forked = 0; 70 if (iovcnt > (int)(sizeof(localiov) / sizeof(localiov[0]))) 71 return ("too many iov's (change code in wall/ttymsg.c)"); 72 73 strlcat(device, line, sizeof(device)); 74 p = device + sizeof(_PATH_DEV) - 1; 75 if (strncmp(p, "pts/", 4) == 0) 76 p += 4; 77 if (strchr(p, '/') != NULL) { 78 /* A slash is an attempt to break security... */ 79 (void) snprintf(errbuf, sizeof(errbuf), 80 "Too many '/' in \"%s\"", device); 81 return (errbuf); 82 } 83 84 /* 85 * open will fail on slip lines or exclusive-use lines 86 * if not running as root; not an error. 87 */ 88 if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) { 89 if (errno == EBUSY || errno == EACCES) 90 return (NULL); 91 (void) snprintf(errbuf, sizeof(errbuf), "%s: %s", device, 92 strerror(errno)); 93 return (errbuf); 94 } 95 96 for (cnt = 0, left = 0; cnt < iovcnt; ++cnt) 97 left += iov[cnt].iov_len; 98 99 for (;;) { 100 wret = writev(fd, iov, iovcnt); 101 if (wret >= left) 102 break; 103 if (wret >= 0) { 104 left -= wret; 105 if (iov != localiov) { 106 bcopy(iov, localiov, 107 iovcnt * sizeof(struct iovec)); 108 iov = localiov; 109 } 110 for (cnt = 0; (size_t)wret >= iov->iov_len; ++cnt) { 111 wret -= iov->iov_len; 112 ++iov; 113 --iovcnt; 114 } 115 if (wret) { 116 iov->iov_base = (char *)iov->iov_base + wret; 117 iov->iov_len -= wret; 118 } 119 continue; 120 } 121 if (errno == EWOULDBLOCK) { 122 int cpid; 123 124 if (forked) { 125 (void) close(fd); 126 _exit(1); 127 } 128 cpid = fork(); 129 if (cpid < 0) { 130 (void) snprintf(errbuf, sizeof(errbuf), 131 "fork: %s", strerror(errno)); 132 (void) close(fd); 133 return (errbuf); 134 } 135 if (cpid) { /* parent */ 136 (void) close(fd); 137 return (NULL); 138 } 139 forked++; 140 /* wait at most tmout seconds */ 141 (void) signal(SIGALRM, SIG_DFL); 142 (void) signal(SIGTERM, SIG_DFL); /* XXX */ 143 (void) sigsetmask(0); 144 (void) alarm((u_int)tmout); 145 (void) fcntl(fd, F_SETFL, 0); /* clear O_NONBLOCK */ 146 continue; 147 } 148 /* 149 * We get ENODEV on a slip line if we're running as root, 150 * and EIO if the line just went away. 151 */ 152 if (errno == ENODEV || errno == EIO) 153 break; 154 (void) close(fd); 155 if (forked) 156 _exit(1); 157 (void) snprintf(errbuf, sizeof(errbuf), 158 "%s: %s", device, strerror(errno)); 159 return (errbuf); 160 } 161 162 (void) close(fd); 163 if (forked) 164 _exit(0); 165 return (NULL); 166 } 167