1 /* 2 * Copyright (c) 1989, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)ttymsg.c 8.2 (Berkeley) 11/16/93"; 10 #endif /* not lint */ 11 12 #include <sys/types.h> 13 #include <sys/uio.h> 14 #include <signal.h> 15 #include <fcntl.h> 16 #include <dirent.h> 17 #include <errno.h> 18 #include <paths.h> 19 #include <unistd.h> 20 #include <stdio.h> 21 #include <string.h> 22 #include <stdlib.h> 23 24 /* 25 * Display the contents of a uio structure on a terminal. Used by wall(1), 26 * syslogd(8), and talkd(8). Forks and finishes in child if write would block, 27 * waiting up to tmout seconds. Returns pointer to error string on unexpected 28 * error; string is not newline-terminated. Various "normal" errors are 29 * ignored (exclusive-use, lack of permission, etc.). 30 */ 31 char * 32 ttymsg(iov, iovcnt, line, tmout) 33 struct iovec *iov; 34 int iovcnt; 35 char *line; 36 int tmout; 37 { 38 static char device[MAXNAMLEN] = _PATH_DEV; 39 static char errbuf[1024]; 40 register int cnt, fd, left, wret; 41 struct iovec localiov[6]; 42 int forked = 0; 43 44 if (iovcnt > sizeof(localiov) / sizeof(localiov[0])) 45 return ("too many iov's (change code in wall/ttymsg.c)"); 46 47 (void) strcpy(device + sizeof(_PATH_DEV) - 1, line); 48 if (strchr(device + sizeof(_PATH_DEV) - 1, '/')) { 49 /* A slash is an attempt to break security... */ 50 (void) snprintf(errbuf, sizeof(errbuf), "'/' in \"%s\"", 51 device); 52 return (errbuf); 53 } 54 55 /* 56 * open will fail on slip lines or exclusive-use lines 57 * if not running as root; not an error. 58 */ 59 if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) { 60 if (errno == EBUSY || errno == EACCES) 61 return (NULL); 62 (void) snprintf(errbuf, sizeof(errbuf), 63 "%s: %s", device, strerror(errno)); 64 return (errbuf); 65 } 66 67 for (cnt = left = 0; cnt < iovcnt; ++cnt) 68 left += iov[cnt].iov_len; 69 70 for (;;) { 71 wret = writev(fd, iov, iovcnt); 72 if (wret >= left) 73 break; 74 if (wret >= 0) { 75 left -= wret; 76 if (iov != localiov) { 77 bcopy(iov, localiov, 78 iovcnt * sizeof(struct iovec)); 79 iov = localiov; 80 } 81 for (cnt = 0; wret >= iov->iov_len; ++cnt) { 82 wret -= iov->iov_len; 83 ++iov; 84 --iovcnt; 85 } 86 if (wret) { 87 iov->iov_base += wret; 88 iov->iov_len -= wret; 89 } 90 continue; 91 } 92 if (errno == EWOULDBLOCK) { 93 int cpid, off = 0; 94 95 if (forked) { 96 (void) close(fd); 97 _exit(1); 98 } 99 cpid = fork(); 100 if (cpid < 0) { 101 (void) snprintf(errbuf, sizeof(errbuf), 102 "fork: %s", strerror(errno)); 103 (void) close(fd); 104 return (errbuf); 105 } 106 if (cpid) { /* parent */ 107 (void) close(fd); 108 return (NULL); 109 } 110 forked++; 111 /* wait at most tmout seconds */ 112 (void) signal(SIGALRM, SIG_DFL); 113 (void) signal(SIGTERM, SIG_DFL); /* XXX */ 114 (void) sigsetmask(0); 115 (void) alarm((u_int)tmout); 116 (void) fcntl(fd, O_NONBLOCK, &off); 117 continue; 118 } 119 /* 120 * We get ENODEV on a slip line if we're running as root, 121 * and EIO if the line just went away. 122 */ 123 if (errno == ENODEV || errno == EIO) 124 break; 125 (void) close(fd); 126 if (forked) 127 _exit(1); 128 (void) snprintf(errbuf, sizeof(errbuf), 129 "%s: %s", device, strerror(errno)); 130 return (errbuf); 131 } 132 133 (void) close(fd); 134 if (forked) 135 _exit(0); 136 return (NULL); 137 } 138