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 *
ttymsg(iov,iovcnt,line,tmout)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