xref: /titanic_44/usr/src/lib/libast/common/sfio/sfpkrd.c (revision 1e4c938b57d1656808e4112127ff1dce3eba5314)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Phong Vo <kpv@research.att.com>                    *
20 *                                                                      *
21 ***********************************************************************/
22 #include	"sfhdr.h"
23 #if !_PACKAGE_ast
24 #ifndef FIONREAD
25 #if _sys_ioctl
26 #include	<sys/ioctl.h>
27 #endif
28 #endif
29 #endif
30 
31 /*	Read/Peek a record from an unseekable device
32 **
33 **	Written by Kiem-Phong Vo.
34 */
35 
36 #define STREAM_PEEK	001
37 #define SOCKET_PEEK	002
38 
39 #if __STD_C
40 ssize_t sfpkrd(int fd, Void_t* argbuf, size_t n, int rc, long tm, int action)
41 #else
42 ssize_t sfpkrd(fd, argbuf, n, rc, tm, action)
43 int	fd;	/* file descriptor */
44 Void_t*	argbuf;	/* buffer to read data */
45 size_t	n;	/* buffer size */
46 int	rc;	/* record character */
47 long	tm;	/* time-out */
48 int	action;	/* >0: peeking, if rc>=0, get action records,
49 		   <0: no peeking, if rc>=0, get -action records,
50 		   =0: no peeking, if rc>=0, must get a single record
51 		*/
52 #endif
53 {
54 	reg ssize_t	r;
55 	reg int		ntry, t;
56 	reg char	*buf = (char*)argbuf, *endbuf;
57 
58 	if(rc < 0 && tm < 0 && action <= 0)
59 		return sysreadf(fd,buf,n);
60 
61 	t = (action > 0 || rc >= 0) ? (STREAM_PEEK|SOCKET_PEEK) : 0;
62 #if !_stream_peek
63 	t &= ~STREAM_PEEK;
64 #endif
65 #if !_socket_peek
66 	t &= ~SOCKET_PEEK;
67 #endif
68 
69 	for(ntry = 0; ntry < 2; ++ntry)
70 	{
71 		r = -1;
72 #if _stream_peek
73 		if((t&STREAM_PEEK) && (ntry == 1 || tm < 0) )
74 		{
75 			struct strpeek	pbuf;
76 			pbuf.flags = 0;
77 			pbuf.ctlbuf.maxlen = -1;
78 			pbuf.ctlbuf.len = 0;
79 			pbuf.ctlbuf.buf = NIL(char*);
80 			pbuf.databuf.maxlen = n;
81 			pbuf.databuf.buf = buf;
82 			pbuf.databuf.len = 0;
83 
84 			if((r = ioctl(fd,I_PEEK,&pbuf)) < 0)
85 			{	if(errno == EINTR)
86 					return -1;
87 				t &= ~STREAM_PEEK;
88 			}
89 			else
90 			{	t &= ~SOCKET_PEEK;
91 				if(r > 0 && (r = pbuf.databuf.len) <= 0)
92 				{	if(action <= 0)	/* read past eof */
93 						r = sysreadf(fd,buf,1);
94 					return r;
95 				}
96 				if(r == 0)
97 					r = -1;
98 				else if(r > 0)
99 					break;
100 			}
101 		}
102 #endif /* stream_peek */
103 
104 		if(ntry == 1)
105 			break;
106 
107 		/* poll or select to see if data is present.  */
108 		while(tm >= 0 || action > 0 ||
109 			/* block until there is data before peeking again */
110 			((t&STREAM_PEEK) && rc >= 0) ||
111 			/* let select be interrupted instead of recv which autoresumes */
112 			(t&SOCKET_PEEK) )
113 		{	r = -2;
114 #if _lib_poll
115 			if(r == -2)
116 			{
117 				struct pollfd	po;
118 				po.fd = fd;
119 				po.events = POLLIN;
120 				po.revents = 0;
121 
122 				if((r = SFPOLL(&po,1,tm)) < 0)
123 				{	if(errno == EINTR)
124 						return -1;
125 					else if(errno == EAGAIN)
126 					{	errno = 0;
127 						continue;
128 					}
129 					else	r = -2;
130 				}
131 				else	r = (po.revents&POLLIN) ? 1 : -1;
132 			}
133 #endif /*_lib_poll*/
134 #if _lib_select
135 			if(r == -2)
136 			{
137 #if _hpux_threads && vt_threaded
138 #define fd_set	int
139 #endif
140 				fd_set		rd;
141 				struct timeval	tmb, *tmp;
142 				FD_ZERO(&rd);
143 				FD_SET(fd,&rd);
144 				if(tm < 0)
145 					tmp = NIL(struct timeval*);
146 				else
147 				{	tmp = &tmb;
148 					tmb.tv_sec = tm/SECOND;
149 					tmb.tv_usec = (tm%SECOND)*SECOND;
150 				}
151 				r = select(fd+1,&rd,NIL(fd_set*),NIL(fd_set*),tmp);
152 				if(r < 0)
153 				{	if(errno == EINTR)
154 						return -1;
155 					else if(errno == EAGAIN)
156 					{	errno = 0;
157 						continue;
158 					}
159 					else	r = -2;
160 				}
161 				else	r = FD_ISSET(fd,&rd) ? 1 : -1;
162 			}
163 #endif /*_lib_select*/
164 			if(r == -2)
165 			{
166 #if !_lib_poll && !_lib_select	/* both poll and select can't be used */
167 #ifdef FIONREAD			/* quick and dirty check for availability */
168 				long	nsec = tm < 0 ? 0 : (tm+999)/1000;
169 				while(nsec > 0 && r < 0)
170 				{	long	avail = -1;
171 					if((r = ioctl(fd,FIONREAD,&avail)) < 0)
172 					{	if(errno == EINTR)
173 							return -1;
174 						else if(errno == EAGAIN)
175 						{	errno = 0;
176 							continue;
177 						}
178 						else	/* ioctl failed completely */
179 						{	r = -2;
180 							break;
181 						}
182 					}
183 					else	r = avail <= 0 ? -1 : (ssize_t)avail;
184 
185 					if(r < 0 && nsec-- > 0)
186 						sleep(1);
187 				}
188 #endif
189 #endif
190 			}
191 
192 			if(r > 0)		/* there is data now */
193 			{	if(action <= 0 && rc < 0)
194 					return sysreadf(fd,buf,n);
195 				else	r = -1;
196 			}
197 			else if(tm >= 0)	/* timeout exceeded */
198 				return -1;
199 			else	r = -1;
200 			break;
201 		}
202 
203 #if _socket_peek
204 		if(t&SOCKET_PEEK)
205 		{
206 #if __MACH__ && __APPLE__
207 			/*
208 			 * work around macos 10.4 recv(MSG_PEEK) bug that consumes pipe() data
209 			 */
210 
211 			struct stat	st;
212 			static int	recv_peek_ok;
213 			if (!recv_peek_ok)
214 			{
215 				int	fds[2];
216 				char	tst[2];
217 				tst[0] = 'a';
218 				tst[1] = 'z';
219 				recv_peek_ok = (!pipe(fds) && write(fds[1], tst, 2) && recv(fds[0], tst, 1, MSG_PEEK) == 1 && tst[0] == 'a' && recv(fds[0], tst, 1, MSG_PEEK) == 1 && tst[0] == 'a') ? 1 : -1;
220 				close(fds[0]);
221 				close(fds[1]);
222 			}
223 			if (recv_peek_ok < 0 && !fstat(fd, &st) && !S_ISSOCK(st.st_mode))
224 			{
225 				r = -1;
226 				t &= ~SOCKET_PEEK;
227 			}
228 			else
229 #endif
230 			while((r = recv(fd,(char*)buf,n,MSG_PEEK)) < 0)
231 			{	if(errno == EINTR)
232 					return -1;
233 				else if(errno == EAGAIN)
234 				{	errno = 0;
235 					continue;
236 				}
237 				t &= ~SOCKET_PEEK;
238 				break;
239 			}
240 			if(r >= 0)
241 			{	t &= ~STREAM_PEEK;
242 				if(r > 0)
243 					break;
244 				else	/* read past eof */
245 				{	if(action <= 0)
246 						r = sysreadf(fd,buf,1);
247 					return r;
248 				}
249 			}
250 		}
251 #endif
252 	}
253 
254 	if(r < 0)
255 	{	if(tm >= 0 || action > 0)
256 			return -1;
257 		else /* get here means: tm < 0 && action <= 0 && rc >= 0 */
258 		{	/* number of records read at a time */
259 			if((action = action ? -action : 1) > (int)n)
260 				action = n;
261 			r = 0;
262 			while((t = sysreadf(fd,buf,action)) > 0)
263 			{	r += t;
264 				for(endbuf = buf+t; buf < endbuf;)
265 					if(*buf++ == rc)
266 						action -= 1;
267 				if(action == 0 || (int)(n-r) < action)
268 					break;
269 			}
270 			return r == 0 ? t : r;
271 		}
272 	}
273 
274 	/* successful peek, find the record end */
275 	if(rc >= 0)
276 	{	reg char*	sp;
277 
278 		t = action == 0 ? 1 : action < 0 ? -action : action;
279 		for(endbuf = (sp = buf)+r; sp < endbuf; )
280 			if(*sp++ == rc)
281 				if((t -= 1) == 0)
282 					break;
283 		r = sp - buf;
284 	}
285 
286 	/* advance */
287 	if(action <= 0)
288 		r = sysreadf(fd,buf,r);
289 
290 	return r;
291 }
292