1    	/*
2    	  Copyright Red Hat, Inc. 2002-2003
3    	
4    	  This program is free software; you can redistribute it and/or modify it
5    	  under the terms of the GNU General Public License as published by the
6    	  Free Software Foundation; either version 2, or (at your option) any
7    	  later version.
8    	
9    	  This program is distributed in the hope that it will be useful, but
10   	  WITHOUT ANY WARRANTY; without even the implied warranty of
11   	  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   	  General Public License for more details.
13   	
14   	  You should have received a copy of the GNU General Public License
15   	  along with this program; see the file COPYING.  If not, write to the
16   	  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge,
17   	  MA 02139, USA.
18   	*/
19   	/** @file
20   	 * Wrapper functions around read/write/select to retry in the event
21   	 * of interrupts.
22   	 */
23   	
24   	#include "config.h"
25   	
26   	#include <unistd.h>
27   	#include <sys/types.h>
28   	#include <fcntl.h>
29   	#include <errno.h>
30   	
31   	#include "fdops.h"
32   	
33   	/**
34   	 * This is a wrapper around select which will retry in the case we receive
35   	 * EINTR.  This is necessary for _read_retry, since it wouldn't make sense
36   	 * to have _read_retry terminate if and only if two EINTRs were received
37   	 * in a row - one during the read() call, one during the select call...
38   	 *
39   	 * See select(2) for description of parameters.
40   	 */
41   	int
42   	_select_retry(int fdmax, fd_set * rfds, fd_set * wfds, fd_set * xfds,
43   		       struct timeval *timeout)
44   	{
45   		int rv;
46   	
47   		while (1) {
48   			rv = select(fdmax, rfds, wfds, xfds, timeout);
49   			if (rv == -1) {
50   				/* return on EBADF/EINVAL/ENOMEM; continue on EINTR/EAGAIN/ENOMEM */
51   				if (errno == EINTR || errno == EAGAIN || errno == ENOMEM)
52   					continue;
53   			}
54   			return rv;
55   		}
56   	}
57   	
58   	/**
59   	 * Retries a write in the event of a non-blocked interrupt signal.
60   	 *
61   	 * @param fd		File descriptor to which we are writing.
62   	 * @param buf		Data buffer to send.
63   	 * @param count		Number of bytes in buf to send.
64   	 * @param timeout	(struct timeval) telling us how long we should retry.
65   	 * @return		The number of bytes written to the file descriptor,
66   	 * 			or -1 on error (with errno set appropriately).
67   	 */
68   	ssize_t
69   	_write_retry(int fd, void *buf, int count, struct timeval * timeout)
70   	{
71   		int n, total = 0, remain = count, rv = 0;
72   		fd_set wfds, xfds;
(1) Event var_assign_parm: Assigning: "tmp_buf" = "buf".
Also see events: [read_value]
73   		char *tmp_buf = (char *)buf;
74   	
(2) Event cond_true: Condition "total < count", taking true branch.
75   		while (total < count) {
76   	
77   			/* Create the write FD set of 1... */
78   			FD_ZERO(&wfds);
79   			FD_SET(fd, &wfds);
80   			FD_ZERO(&xfds);
81   			FD_SET(fd, &xfds);
82   	
83   			/* wait for the fd to be available for writing */
84   			rv = _select_retry(fd + 1, NULL, &wfds, &xfds, timeout);
(3) Event cond_false: Condition "rv == -1", taking false branch.
85   			if (rv == -1)
86   				return -1;
(4) Event else_branch: Reached else branch.
(5) Event cond_false: Condition "rv == 0", taking false branch.
87   			else if (rv == 0) {
88   				errno = ETIMEDOUT;
89   				return -1;
(6) Event if_end: End of if statement.
90   			}
91   	
(7) Event cond_false: Condition "(xfds.fds_bits[fd / (64 /* 8 * (int)sizeof (__fd_mask) */)] & (__fd_mask)(1UL << fd % (64 /* 8 * (int)sizeof (__fd_mask) */))) != 0", taking false branch.
92   			if (FD_ISSET(fd, &xfds)) {
93   				errno = EPIPE;
94   				return -1;
(8) Event if_end: End of if statement.
95   			}
96   	
97   			/* 
98   			 * Attempt to write to fd
99   			 */
(9) Event read_value: Reading value "*(tmp_buf + total)" when calling "write".
Also see events: [var_assign_parm]
100  			n = write(fd, tmp_buf + total, remain);
101  	
102  			/*
103  			 * When we know our fd was select()ed and we receive 0 bytes
104  			 * when we write, the fd was closed.
105  			 */
(10) Event cond_true: Condition "n == 0", taking true branch.
(11) Event cond_true: Condition "rv == 1", taking true branch.
106  			if ((n == 0) && (rv == 1)) {
107  				errno = EPIPE;
108  				return -1;
109  			}
110  	
111  			if (n == -1) {
112  				if ((errno == EAGAIN) || (errno == EINTR)) {
113  					/* 
114  					 * Not ready?
115  					 */
116  					continue;
117  				}
118  	
119  				/* Other errors: EIO, EINVAL, etc */
120  				return -1;
121  			}
122  	
123  			total += n;
124  			remain -= n;
125  		}
126  	
127  		return total;
128  	}
129  	
130  	/**
131  	 * Retry reads until we (a) time out or (b) get our data.  Of course, if
132  	 * timeout is NULL, it'll wait forever.
133  	 *
134  	 * @param sockfd	File descriptor we want to read from.
135  	 * @param buf		Preallocated buffer into which we will read data.
136  	 * @param count		Number of bytes to read.
137  	 * @param timeout	(struct timeval) describing how long we should retry.
138  	 * @return 		The number of bytes read on success, or -1 on failure.
139  	 			Note that we will always return (count) or (-1).
140  	 */
141  	ssize_t
142  	_read_retry(int sockfd, void *buf, int count, struct timeval * timeout)
143  	{
144  		int n, total = 0, remain = count, rv = 0;
145  		fd_set rfds, xfds;
146  		char *tmp_buf = (char *)buf;
147  	
148  		while (total < count) {
149  			FD_ZERO(&rfds);
150  			FD_SET(sockfd, &rfds);
151  			FD_ZERO(&xfds);
152  			FD_SET(sockfd, &xfds);
153  			
154  			/*
155  			 * Select on the socket, in case it closes while we're not
156  			 * looking...
157  			 */
158  			rv = _select_retry(sockfd + 1, &rfds, NULL, &xfds, timeout);
159  			if (rv == -1)
160  				return -1;
161  			else if (rv == 0) {
162  				errno = ETIMEDOUT;
163  				return -1;
164  			}
165  	
166  			if (FD_ISSET(sockfd, &xfds)) {
167  				errno = EPIPE;
168  				return -1;
169  			}
170  	
171  			/* 
172  			 * Attempt to read off the socket 
173  			 */
174  			n = read(sockfd, tmp_buf + total, remain);
175  	
176  			/*
177  			 * When we know our socket was select()ed and we receive 0 bytes
178  			 * when we read, the socket was closed.
179  			 */
180  			if ((n == 0) && (rv == 1)) {
181  				errno = EPIPE;
182  				return -1;
183  			}
184  	
185  			if (n == -1) {
186  				if ((errno == EAGAIN) || (errno == EINTR)) {
187  					/* 
188  					 * Not ready? Wait for data to become available
189  					 */
190  					continue;
191  				}
192  	
193  				/* Other errors: EPIPE, EINVAL, etc */
194  				return -1;
195  			}
196  	
197  			total += n;
198  			remain -= n;
199  		}
200  	
201  		return total;
202  	}
203