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