xref: /original-bsd/usr.bin/wall/ttymsg.c (revision 95ecee29)
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